From jic23 at kernel.org Sun Aug 1 03:27:14 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sat, 31 Jul 2021 18:27:14 +0100 Subject: [v2 1/8] dt-bindings: iio: adc: rename the aspeed adc yaml In-Reply-To: References: <20210723081621.29477-1-billy_tsai@aspeedtech.com> <20210723081621.29477-2-billy_tsai@aspeedtech.com> <20210723154456.00006744@Huawei.com> Message-ID: <20210731182714.7197bdcc@jic23-huawei> On Thu, 29 Jul 2021 14:31:35 -0600 Rob Herring wrote: > On Mon, Jul 26, 2021 at 06:53:07AM +0000, Billy Tsai wrote: > > Hi Jonathan, > > > > On 2021/7/23, 10:45 PM, "Jonathan Cameron" wrote: > > > > On Fri, 23 Jul 2021 16:16:14 +0800 > > Billy Tsai wrote: > > > > > > The aspeed,ast2400-adc.yaml not only descriptor the bindings of ast2400. > > > > Rename it to aspeed,adc.yaml for all of the aspeed adc bindings. > > > > > > > > Signed-off-by: Billy Tsai > > > > > We try to avoid 'wild' card type namings most of the time and instead > > > name after a particular part number. I say try because clearly > > > we let a few in over the years :( > > > > > It is very hard to know if this binding will apply to 'all' future > > > aspeed ADCs. > > > > > As such I'm not sure this particular rename makes sense. > > > > If I want to extend the yaml file to compatible more versions of the aspeed adc. > > Would you suggest to add new files call aspeed,ast2600-adc.yaml or just append it > > to the aspeed,ast2400-adc.yaml? > > If 2600 is not backwards compatible with 2400, then probably a new > schema file. Given you are adding new properties (which only apply to > 2600?), then most likely a new schema file. Depends at which point there > are too many conditional (if/then/else) schemas. Agreed. It's a judgement call you need to make on when it is worth the new file. Note that doesn't have anything to do with splitting the driver. We have mulitple binding files for single drivers and for that matter multiple drivers for single binding files. If it is 'compatible' enough to not make the file to complex, then add to the existing ast2400 file without renaming. Jonathan > > Rob From iwona.winiarska at intel.com Mon Aug 2 21:37:30 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Mon, 2 Aug 2021 11:37:30 +0000 Subject: [PATCH 13/14] docs: hwmon: Document PECI drivers In-Reply-To: <20210727225808.GU8018@packtop> References: <20210712220447.957418-1-iwona.winiarska@intel.com> <20210712220447.957418-14-iwona.winiarska@intel.com> <20210727225808.GU8018@packtop> Message-ID: On Tue, 2021-07-27 at 22:58 +0000, Zev Weiss wrote: > On Mon, Jul 12, 2021 at 05:04:46PM CDT, Iwona Winiarska wrote: > > From: Jae Hyun Yoo > > > > Add documentation for peci-cputemp driver that provides DTS thermal > > readings for CPU packages and CPU cores and peci-dimmtemp driver that > > provides DTS thermal readings for DIMMs. > > > > Signed-off-by: Jae Hyun Yoo > > Co-developed-by: Iwona Winiarska > > Signed-off-by: Iwona Winiarska > > Reviewed-by: Pierre-Louis Bossart > > --- > > Documentation/hwmon/index.rst???????? |? 2 + > > Documentation/hwmon/peci-cputemp.rst? | 93 +++++++++++++++++++++++++++ > > Documentation/hwmon/peci-dimmtemp.rst | 58 +++++++++++++++++ > > MAINTAINERS?????????????????????????? |? 2 + > > 4 files changed, 155 insertions(+) > > create mode 100644 Documentation/hwmon/peci-cputemp.rst > > create mode 100644 Documentation/hwmon/peci-dimmtemp.rst > > > > diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst > > index bc01601ea81a..cc76b5b3f791 100644 > > --- a/Documentation/hwmon/index.rst > > +++ b/Documentation/hwmon/index.rst > > @@ -154,6 +154,8 @@ Hardware Monitoring Kernel Drivers > > ?? pcf8591 > > ?? pim4328 > > ?? pm6764tr > > +?? peci-cputemp > > +?? peci-dimmtemp > > ?? pmbus > > ?? powr1220 > > ?? pxe1610 > > diff --git a/Documentation/hwmon/peci-cputemp.rst > > b/Documentation/hwmon/peci-cputemp.rst > > new file mode 100644 > > index 000000000000..d3a218ba810a > > --- /dev/null > > +++ b/Documentation/hwmon/peci-cputemp.rst > > @@ -0,0 +1,93 @@ > > +.. SPDX-License-Identifier: GPL-2.0-only > > + > > +Kernel driver peci-cputemp > > +========================== > > + > > +Supported chips: > > +???????One of Intel server CPUs listed below which is connected to a PECI > > bus. > > +???????????????* Intel Xeon E5/E7 v3 server processors > > +???????????????????????Intel Xeon E5-14xx v3 family > > +???????????????????????Intel Xeon E5-24xx v3 family > > +???????????????????????Intel Xeon E5-16xx v3 family > > +???????????????????????Intel Xeon E5-26xx v3 family > > +???????????????????????Intel Xeon E5-46xx v3 family > > +???????????????????????Intel Xeon E7-48xx v3 family > > +???????????????????????Intel Xeon E7-88xx v3 family > > +???????????????* Intel Xeon E5/E7 v4 server processors > > +???????????????????????Intel Xeon E5-16xx v4 family > > +???????????????????????Intel Xeon E5-26xx v4 family > > +???????????????????????Intel Xeon E5-46xx v4 family > > +???????????????????????Intel Xeon E7-48xx v4 family > > +???????????????????????Intel Xeon E7-88xx v4 family > > +???????????????* Intel Xeon Scalable server processors > > +???????????????????????Intel Xeon D family > > +???????????????????????Intel Xeon Bronze family > > +???????????????????????Intel Xeon Silver family > > +???????????????????????Intel Xeon Gold family > > +???????????????????????Intel Xeon Platinum family > > + > > +???????Datasheet: Available from http://www.intel.com/design/literature.htm > > + > > +Author: Jae Hyun Yoo > > + > > +Description > > +----------- > > + > > +This driver implements a generic PECI hwmon feature which provides Digital > > +Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that > > are > > +accessible via the processor PECI interface. > > + > > +All temperature values are given in millidegree Celsius and will be > > measurable > > +only when the target CPU is powered on. > > + > > +Sysfs interface > > +------------------- > > + > > +======================= > > ======================================================= > > +temp1_label????????????"Die" > > +temp1_input????????????Provides current die temperature of the CPU package. > > +temp1_max??????????????Provides thermal control temperature of the CPU > > package > > +???????????????????????which is also known as Tcontrol. > > +temp1_crit?????????????Provides shutdown temperature of the CPU package > > which > > +???????????????????????is also known as the maximum processor junction > > +???????????????????????temperature, Tjmax or Tprochot. > > +temp1_crit_hyst????????????????Provides the hysteresis value from Tcontrol > > to Tjmax of > > +???????????????????????the CPU package. > > + > > +temp2_label????????????"DTS" > > +temp2_input????????????Provides current DTS temperature of the CPU package. > > Would this be a good place to note the slightly counter-intuitive nature > of DTS readings?? i.e. add something along the lines of "The DTS sensor > produces a delta relative to Tjmax, so negative values are normal and > values approaching zero are hot."? (In my experience people who aren't > already familiar with it tend to think something's wrong when a CPU > temperature reading shows -50C.) I believe that what you're referring to is a result of "GetTemp", and we're using it to calculate "Die" sensor values (temp1). The sensor value is absolute - we don't expose "raw" thermal sensor value (delta) anywhere. DTS sensor is exposing temperature value scaled to fit DTS 2.0 thermal profile: https://www.intel.com/content/www/us/en/processors/xeon/scalable/xeon-scalable-thermal-guide.html (section 5.2.3.2) Similar to "Die" sensor - it's also exposed in absolute form. I'll try to change description to avoid confusion. > > > +temp2_max??????????????Provides thermal control temperature of the CPU > > package > > +???????????????????????which is also known as Tcontrol. > > +temp2_crit?????????????Provides shutdown temperature of the CPU package which > > +???????????????????????is also known as the maximum processor junction > > +???????????????????????temperature, Tjmax or Tprochot. > > +temp2_crit_hyst????????????????Provides the hysteresis value from Tcontrol to > > Tjmax of > > +???????????????????????the CPU package. > > + > > +temp3_label????????????"Tcontrol" > > +temp3_input????????????Provides current Tcontrol temperature of the CPU > > +???????????????????????package which is also known as Fan Temperature target. > > +???????????????????????Indicates the relative value from thermal monitor trip > > +???????????????????????temperature at which fans should be engaged. > > +temp3_crit?????????????Provides Tcontrol critical value of the CPU package > > +???????????????????????which is same to Tjmax. > > + > > +temp4_label????????????"Tthrottle" > > +temp4_input????????????Provides current Tthrottle temperature of the CPU > > +???????????????????????package. Used for throttling temperature. If this > > value > > +???????????????????????is allowed and lower than Tjmax - the throttle will > > +???????????????????????occur and reported at lower than Tjmax. > > + > > +temp5_label????????????"Tjmax" > > +temp5_input????????????Provides the maximum junction temperature, Tjmax of > > the > > +???????????????????????CPU package. > > + > > +temp[6-N]_label????????????????Provides string "Core X", where X is resolved > > core > > +???????????????????????number. > > +temp[6-N]_input????????????????Provides current temperature of each core. > > +temp[6-N]_max??????????Provides thermal control temperature of the core. > > +temp[6-N]_crit?????????Provides shutdown temperature of the core. > > +temp[6-N]_crit_hyst????Provides the hysteresis value from Tcontrol to Tjmax > > of > > +???????????????????????the core. > > I only see *_label and *_input for the per-core temperature sensors, no > *_max, *_crit, or *_crit_hyst. You're right - this should be removed from documentation. > > > + > > +======================= > > ======================================================= > > diff --git a/Documentation/hwmon/peci-dimmtemp.rst b/Documentation/hwmon/peci- > > dimmtemp.rst > > new file mode 100644 > > index 000000000000..1778d9317e43 > > --- /dev/null > > +++ b/Documentation/hwmon/peci-dimmtemp.rst > > @@ -0,0 +1,58 @@ > > +.. SPDX-License-Identifier: GPL-2.0 > > + > > +Kernel driver peci-dimmtemp > > +=========================== > > + > > +Supported chips: > > +???????One of Intel server CPUs listed below which is connected to a PECI > > bus. > > +???????????????* Intel Xeon E5/E7 v3 server processors > > +???????????????????????Intel Xeon E5-14xx v3 family > > +???????????????????????Intel Xeon E5-24xx v3 family > > +???????????????????????Intel Xeon E5-16xx v3 family > > +???????????????????????Intel Xeon E5-26xx v3 family > > +???????????????????????Intel Xeon E5-46xx v3 family > > +???????????????????????Intel Xeon E7-48xx v3 family > > +???????????????????????Intel Xeon E7-88xx v3 family > > +???????????????* Intel Xeon E5/E7 v4 server processors > > +???????????????????????Intel Xeon E5-16xx v4 family > > +???????????????????????Intel Xeon E5-26xx v4 family > > +???????????????????????Intel Xeon E5-46xx v4 family > > +???????????????????????Intel Xeon E7-48xx v4 family > > +???????????????????????Intel Xeon E7-88xx v4 family > > +???????????????* Intel Xeon Scalable server processors > > +???????????????????????Intel Xeon D family > > +???????????????????????Intel Xeon Bronze family > > +???????????????????????Intel Xeon Silver family > > +???????????????????????Intel Xeon Gold family > > +???????????????????????Intel Xeon Platinum family > > + > > +???????Datasheet: Available from http://www.intel.com/design/literature.htm > > + > > +Author: Jae Hyun Yoo > > + > > +Description > > +----------- > > + > > +This driver implements a generic PECI hwmon feature which provides Digital > > +Thermal Sensor (DTS) thermal readings of DIMM components that are accessible > > +via the processor PECI interface. > > I had thought "DTS" referred to a fairly specific sensor in the CPU; is > the same term also used for DIMM temp sensors or is the mention of it > here a copy/paste error? Yeah - it should be "Temperature Sensor on DIMM". Thanks -Iwona > > > + > > +All temperature values are given in millidegree Celsius and will be > > measurable > > +only when the target CPU is powered on. > > + > > +Sysfs interface > > +------------------- > > + > > +======================= > > ======================================================= > > + > > +temp[N]_label??????????Provides string "DIMM CI", where C is DIMM channel and > > +???????????????????????I is DIMM index of the populated DIMM. > > +temp[N]_input??????????Provides current temperature of the populated DIMM. > > +temp[N]_max????????????Provides thermal control temperature of the DIMM. > > +temp[N]_crit???????????Provides shutdown temperature of the DIMM. > > + > > +======================= > > ======================================================= > > + > > +Note: > > +???????DIMM temperature attributes will appear when the client CPU's BIOS > > +???????completes memory training and testing. > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 35ba9e3646bd..d16da127bbdc 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -14509,6 +14509,8 @@ M:??????Iwona Winiarska > > R:??????Jae Hyun Yoo > > L:??????linux-hwmon at vger.kernel.org > > S:??????Supported > > +F:?????Documentation/hwmon/peci-cputemp.rst > > +F:?????Documentation/hwmon/peci-dimmtemp.rst > > F:??????drivers/hwmon/peci/ > > > > PECI SUBSYSTEM > > -- > > 2.31.1 From iwona.winiarska at intel.com Mon Aug 2 21:39:36 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Mon, 2 Aug 2021 11:39:36 +0000 Subject: [PATCH 13/14] docs: hwmon: Document PECI drivers In-Reply-To: <0521a076-9772-532f-2eab-8870464ca211@roeck-us.net> References: <20210712220447.957418-1-iwona.winiarska@intel.com> <20210712220447.957418-14-iwona.winiarska@intel.com> <20210727225808.GU8018@packtop> <0521a076-9772-532f-2eab-8870464ca211@roeck-us.net> Message-ID: On Tue, 2021-07-27 at 17:49 -0700, Guenter Roeck wrote: > On 7/27/21 3:58 PM, Zev Weiss wrote: > > On Mon, Jul 12, 2021 at 05:04:46PM CDT, Iwona Winiarska wrote: > > > From: Jae Hyun Yoo > > > > > > Add documentation for peci-cputemp driver that provides DTS thermal > > > readings for CPU packages and CPU cores and peci-dimmtemp driver that > > > provides DTS thermal readings for DIMMs. > > > > > > Signed-off-by: Jae Hyun Yoo > > > Co-developed-by: Iwona Winiarska > > > Signed-off-by: Iwona Winiarska > > > Reviewed-by: Pierre-Louis Bossart > > > --- > > > Documentation/hwmon/index.rst???????? |? 2 + > > > Documentation/hwmon/peci-cputemp.rst? | 93 +++++++++++++++++++++++++++ > > > Documentation/hwmon/peci-dimmtemp.rst | 58 +++++++++++++++++ > > > MAINTAINERS?????????????????????????? |? 2 + > > > 4 files changed, 155 insertions(+) > > > create mode 100644 Documentation/hwmon/peci-cputemp.rst > > > create mode 100644 Documentation/hwmon/peci-dimmtemp.rst > > > > > > diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst > > > index bc01601ea81a..cc76b5b3f791 100644 > > > --- a/Documentation/hwmon/index.rst > > > +++ b/Documentation/hwmon/index.rst > > > @@ -154,6 +154,8 @@ Hardware Monitoring Kernel Drivers > > > ??? pcf8591 > > > ??? pim4328 > > > ??? pm6764tr > > > +?? peci-cputemp > > > +?? peci-dimmtemp > > > ??? pmbus > > > ??? powr1220 > > > ??? pxe1610 > > > diff --git a/Documentation/hwmon/peci-cputemp.rst > > > b/Documentation/hwmon/peci-cputemp.rst > > > new file mode 100644 > > > index 000000000000..d3a218ba810a > > > --- /dev/null > > > +++ b/Documentation/hwmon/peci-cputemp.rst > > > @@ -0,0 +1,93 @@ > > > +.. SPDX-License-Identifier: GPL-2.0-only > > > + > > > +Kernel driver peci-cputemp > > > +========================== > > > + > > > +Supported chips: > > > +???????One of Intel server CPUs listed below which is connected to a PECI > > > bus. > > > +???????????????* Intel Xeon E5/E7 v3 server processors > > > +???????????????????????Intel Xeon E5-14xx v3 family > > > +???????????????????????Intel Xeon E5-24xx v3 family > > > +???????????????????????Intel Xeon E5-16xx v3 family > > > +???????????????????????Intel Xeon E5-26xx v3 family > > > +???????????????????????Intel Xeon E5-46xx v3 family > > > +???????????????????????Intel Xeon E7-48xx v3 family > > > +???????????????????????Intel Xeon E7-88xx v3 family > > > +???????????????* Intel Xeon E5/E7 v4 server processors > > > +???????????????????????Intel Xeon E5-16xx v4 family > > > +???????????????????????Intel Xeon E5-26xx v4 family > > > +???????????????????????Intel Xeon E5-46xx v4 family > > > +???????????????????????Intel Xeon E7-48xx v4 family > > > +???????????????????????Intel Xeon E7-88xx v4 family > > > +???????????????* Intel Xeon Scalable server processors > > > +???????????????????????Intel Xeon D family > > > +???????????????????????Intel Xeon Bronze family > > > +???????????????????????Intel Xeon Silver family > > > +???????????????????????Intel Xeon Gold family > > > +???????????????????????Intel Xeon Platinum family > > > + > > > +???????Datasheet: Available from http://www.intel.com/design/literature.htm > > > + > > > +Author: Jae Hyun Yoo > > > + > > > +Description > > > +----------- > > > + > > > +This driver implements a generic PECI hwmon feature which provides Digital > > > +Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that > > > are > > > +accessible via the processor PECI interface. > > > + > > > +All temperature values are given in millidegree Celsius and will be > > > measurable > > > +only when the target CPU is powered on. > > > + > > > +Sysfs interface > > > +------------------- > > > + > > > +======================= > > > ======================================================= > > > +temp1_label????????????"Die" > > > +temp1_input????????????Provides current die temperature of the CPU package. > > > +temp1_max??????????????Provides thermal control temperature of the CPU > > > package > > > +???????????????????????which is also known as Tcontrol. > > > +temp1_crit?????????????Provides shutdown temperature of the CPU package > > > which > > > +???????????????????????is also known as the maximum processor junction > > > +???????????????????????temperature, Tjmax or Tprochot. > > > +temp1_crit_hyst????????????????Provides the hysteresis value from Tcontrol > > > to Tjmax of > > > +???????????????????????the CPU package. > > > + > > > +temp2_label????????????"DTS" > > > +temp2_input????????????Provides current DTS temperature of the CPU package. > > > > Would this be a good place to note the slightly counter-intuitive nature > > of DTS readings?? i.e. add something along the lines of "The DTS sensor > > produces a delta relative to Tjmax, so negative values are normal and > > values approaching zero are hot."? (In my experience people who aren't > > already familiar with it tend to think something's wrong when a CPU > > temperature reading shows -50C.) > > > > All attributes shall follow the ABI, and the driver must translate reported > values to degrees C. If those sensors do not follow the ABI and report something > else, I won't accept the driver. > > Guenter Sure, I believe all attributes already follow the ABI and the reported values are in millidegree Celsius. Thanks -Iwona > From andrew at aj.id.au Tue Aug 3 14:07:07 2021 From: andrew at aj.id.au (Andrew Jeffery) Date: Tue, 03 Aug 2021 13:37:07 +0930 Subject: [RFC PATCH 0/6] leds: Fix pca955x GPIO pin mappings In-Reply-To: References: <20210723075858.376378-1-andrew@aj.id.au> <6cc64039-f82a-4c1e-ad2c-16fad7aa3178@www.fastmail.com> Message-ID: <50aaf381-8cda-4656-9222-f23fda75d3bc@www.fastmail.com> On Thu, 29 Jul 2021, at 17:10, Andy Shevchenko wrote: > On Thu, Jul 29, 2021 at 3:39 AM Andrew Jeffery wrote: > > On Wed, 28 Jul 2021, at 18:43, Andy Shevchenko wrote: > > > On Wed, Jul 28, 2021 at 8:43 AM Andrew Jeffery wrote: > > > > However, userspace would never have > > > > got the results it expected with the existing driver implementation, so > > > > I guess you could argue that no such (useful) userspace exists. Given > > > > that, we could adopt the strategy of always defining a gpiochip > > > > covering the whole pin space, and parts of the devicetree binding just > > > > become redundant. > > > > > > I'm lost now. GPIO has its own userspace ABI, how does it work right > > > now in application to this chip? > > > > As above, it "works" if the GPIOs specified in the devicetree are > > contiguous from line 0. It's broken if they're not. > > So, "it never works" means there is no bug. Now, what we need is to > keep the same enumeration scheme, but if you wish to be used half/half > (or any other ratio), the driver should do like the above mentioned > PWM, i.e. register entire space and depending on the requestor either > proceed with a line or mark it as BUSY. > > Ideally, looking into what the chip can do, this should be indeed > converted to some like pin control + PWM + LED + GPIO drivers. Then > the function in pin mux configuration can show what exactly is enabled > on the certain line(s). So just to clarify, you want both solutions here? 1. A gpiochip that covers the entire pin space 2. A pinmux implementation that manages pin allocation to the different drivers In that case we can largely leave this series as is? We only need to adjust how we configure the gpiochip by dropping the pin-mapping implementation? I don't have a need to implement a PWM driver for it right now but that would make sense to do at some point. Andrew From andrew at aj.id.au Tue Aug 3 14:48:51 2021 From: andrew at aj.id.au (Andrew Jeffery) Date: Tue, 03 Aug 2021 14:18:51 +0930 Subject: [PATCH v6 0/9] ASPEED sgpio driver enhancement. In-Reply-To: References: <20210712100317.23298-1-steven_lee@aspeedtech.com> <20210723031615.GA10457@aspeedtech.com> Message-ID: <58256e8f-6c9a-4ad4-b51e-4048b6feb42a@www.fastmail.com> On Fri, 23 Jul 2021, at 17:00, Bartosz Golaszewski wrote: > On Fri, Jul 23, 2021 at 5:16 AM Steven Lee wrote: > > > > The 07/21/2021 21:27, Bartosz Golaszewski wrote: > > > On Mon, Jul 12, 2021 at 12:03 PM Steven Lee wrote: > > > > > > > > AST2600 SoC has 2 SGPIO master interfaces one with 128 pins another one > > > > with 80 pins, AST2500/AST2400 SoC has 1 SGPIO master interface that > > > > supports up to 80 pins. > > > > In the current driver design, the max number of sgpio pins is hardcoded > > > > in macro MAX_NR_HW_SGPIO and the value is 80. > > > > > > > > For supporting sgpio master interfaces of AST2600 SoC, the patch series > > > > contains the following enhancement: > > > > - Convert txt dt-bindings to yaml. > > > > - Update aspeed-g6 dtsi to support the enhanced sgpio. > > > > - Support muiltiple SGPIO master interfaces. > > > > - Support up to 128 pins by dts ngpios property. > > > > - Pair input/output GPIOs instead of using 0 as GPIO input pin base and > > > > MAX_NR_HW_SGPIO as GPIO output pin base. > > > > - Support wdt reset tolerance. > > > > - Fix irq_chip issues which causes multiple sgpio devices use the same > > > > irq_chip data. > > > > - Replace all of_*() APIs with device_*(). > > > > > > > > Changes from v5: > > > > * Squash v5 patch-05 and patch-06 to one patch. > > > > * Remove MAX_NR_HW_SGPIO and corresponding design to make the gpio > > > > input/output pin base are determined by ngpios. > > > > For example, if MAX_NR_HW_SGPIO is 80 and ngpios is 10, the original > > > > pin order is as follows: > > > > Input: > > > > 0 1 2 3 ... 9 > > > > Output: > > > > 80 81 82 ... 89 > > > > > > > > With the new design, pin order is changed as follows: > > > > Input: > > > > 0 2 4 6 ... 18(ngpios * 2 - 2) > > > > Output: > > > > 1 3 5 7 ... 19(ngpios * 2 - 1) > > > > * Replace ast2600-sgpiom-128 and ast2600-sgpiom-80 compatibles by > > > > ast2600-sgpiom. > > > > * Fix coding style issues. > > > > > > > > Changes from v4: > > > > * Remove ngpios from dtsi > > > > * Add ast2400 and ast2500 platform data. > > > > * Remove unused macros. > > > > * Add ngpios check in a separate patch. > > > > * Fix coding style issues. > > > > > > > > Changes from v3: > > > > * Split dt-bindings patch to 2 patches > > > > * Rename ast2600-sgpiom1 compatible with ast2600-sgiom-128 > > > > * Rename ast2600-sgpiom2 compatible with ast2600-sgiom-80 > > > > * Correct the typo in commit messages. > > > > * Fix coding style issues. > > > > * Replace all of_*() APIs with device_*(). > > > > > > > > Changes from v2: > > > > * Remove maximum/minimum of ngpios from bindings. > > > > * Remove max-ngpios from bindings and dtsi. > > > > * Remove ast2400-sgpiom and ast2500-sgpiom compatibles from dts and > > > > driver. > > > > * Add ast2600-sgpiom1 and ast2600-sgpiom2 compatibles as their max > > > > number of available gpio pins are different. > > > > * Modify functions to pass aspeed_sgpio struct instead of passing > > > > max_ngpios. > > > > * Split sgpio driver patch to 3 patches > > > > > > > > Changes from v1: > > > > * Fix yaml format issues. > > > > * Fix issues reported by kernel test robot. > > > > > > > > Please help to review. > > > > > > > > Thanks, > > > > Steven > > > > > > > > Steven Lee (9): > > > > dt-bindings: aspeed-sgpio: Convert txt bindings to yaml. > > > > dt-bindings: aspeed-sgpio: Add ast2600 sgpio > > > > ARM: dts: aspeed-g6: Add SGPIO node. > > > > ARM: dts: aspeed-g5: Remove ngpios from sgpio node. > > > > gpio: gpio-aspeed-sgpio: Add AST2600 sgpio support > > > > gpio: gpio-aspeed-sgpio: Add set_config function > > > > gpio: gpio-aspeed-sgpio: Move irq_chip to aspeed-sgpio struct > > > > gpio: gpio-aspeed-sgpio: Use generic device property APIs > > > > gpio: gpio-aspeed-sgpio: Return error if ngpios is not multiple of 8. > > > > > > > > .../bindings/gpio/aspeed,sgpio.yaml | 77 ++++++++ > > > > .../devicetree/bindings/gpio/sgpio-aspeed.txt | 46 ----- > > > > arch/arm/boot/dts/aspeed-g5.dtsi | 1 - > > > > arch/arm/boot/dts/aspeed-g6.dtsi | 28 +++ > > > > drivers/gpio/gpio-aspeed-sgpio.c | 178 +++++++++++------- > > > > 5 files changed, 215 insertions(+), 115 deletions(-) > > > > create mode 100644 Documentation/devicetree/bindings/gpio/aspeed,sgpio.yaml > > > > delete mode 100644 Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt > > > > > > > > -- > > > > 2.17.1 > > > > > > > > > > The series looks good to me. Can the DTS and GPIO patches go into > > > v5.15 separately? > > > > > > > Hi Bart, > > > > Thanks for the review. > > Shall we do anything to make the patches go into v5.15 or wait for picking-up? > > > > Steven > > > > > Bart > > It's more of a question to the relevant SoC maintainers. > > Joel, Andrew: can I take the GPIO patches through the GPIO tree and > you'll take the ARM patches separately into v5.15? I think that should be okay. I'll poke Joel. Andrew From joel at jms.id.au Tue Aug 3 15:57:49 2021 From: joel at jms.id.au (Joel Stanley) Date: Tue, 3 Aug 2021 05:57:49 +0000 Subject: [PATCH v6 0/9] ASPEED sgpio driver enhancement. In-Reply-To: <58256e8f-6c9a-4ad4-b51e-4048b6feb42a@www.fastmail.com> References: <20210712100317.23298-1-steven_lee@aspeedtech.com> <20210723031615.GA10457@aspeedtech.com> <58256e8f-6c9a-4ad4-b51e-4048b6feb42a@www.fastmail.com> Message-ID: On Tue, 3 Aug 2021 at 04:49, Andrew Jeffery wrote: > On Fri, 23 Jul 2021, at 17:00, Bartosz Golaszewski wrote: > > Joel, Andrew: can I take the GPIO patches through the GPIO tree and > > you'll take the ARM patches separately into v5.15? > > I think that should be okay. I'll poke Joel. Yes, that's fine. I have merged the first four patches into the aspeed tree. Cheers, Joel From jic23 at kernel.org Sun Aug 1 03:24:29 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sat, 31 Jul 2021 18:24:29 +0100 Subject: [v2 2/8] dt-bindings: iio: adc: Binding ast2600 adc. In-Reply-To: <8D93D26B-AE57-4E38-A1AD-FA62894F2947@aspeedtech.com> References: <20210723081621.29477-1-billy_tsai@aspeedtech.com> <20210723081621.29477-3-billy_tsai@aspeedtech.com> <20210723155141.000039ee@Huawei.com> <8D93D26B-AE57-4E38-A1AD-FA62894F2947@aspeedtech.com> Message-ID: <20210731182334.2be68876@jic23-huawei> On Mon, 26 Jul 2021 07:21:29 +0000 Billy Tsai wrote: > Hi Jonathan, > > Thanks for your review. I will fix them. > About the vref I reply inline. > > On 2021/7/23, 10:52 PM, "Jonathan Cameron" wrote: > > On Fri, 23 Jul 2021 16:16:15 +0800 > Billy Tsai wrote: > > > > + ? Internal or External reference voltage. > > > + ? Support 2 Internal reference voltage 1.2v or 2.5v. > > > + ? Integrate dividing circuit for battery sensing. > > > > > > properties: > > > compatible: > > > enum: > > > - aspeed,ast2400-adc > > > - aspeed,ast2500-adc > > > + - aspeed,ast2600-adc > > > > > > reg: > > > maxItems: 1 > > > @@ -33,6 +45,18 @@ properties: > > > "#io-channel-cells": > > > const: 1 > > > > > > + vref: > > > + minItems: 900 > > > + maxItems: 2700 > > > + default: 2500 > > > + description: > > > + ADC Reference voltage in millivolts. > > > I'm not clear from this description. Is this describing an externally > > connected voltage reference? If so it needs to be done as a regulator. > > If it's a classic high precision reference, the dts can just use > > a fixed regulator. > > In the ast2600, the ADC supports two internal reference voltages of 1.2v or 2.5v, > as well as external voltages. When the user selects a voltage of 1.2v or 2.5v, my > driver will first select to use the internal voltage. Understood. > As you mention at patch #4, you suggest to use two property to handle this feature. > vref: indicate the regulator handler. Like other dt-bindings used. vref-supply would be the binding but otherwise yes. > aspeed,int_vref_mv: indicate the chosen of 1.2v or 2.5v > and use "model_data->vref_fixed" to exclude ast2400 and ast2500 > Is it right? Ideally you would check in the driver and also use something here to allow device trees to be checked automatically. Something like allOf: - if: not: properties: compatible: contains: const: aspeed,ast2600-adc then: int_vref_mv: false that will ensure the property is only used for the ast2600. Note the above is from memory, so may well have incorrect syntax. I've never found yaml intuitive! Thanks, Jonathan > > Thanks > > > > + > > > + battery-sensing: > > > + type: boolean > > > + description: > > > + Inform the driver that last channel will be used to sensor battery. > > > This isn't (I think?) a standard dt binding, so it needs a manufacturer > > prefix. > > > aspeed,battery-sensing > > Best Regards, > Billy Tsai > > From andy.shevchenko at gmail.com Tue Aug 3 20:33:38 2021 From: andy.shevchenko at gmail.com (Andy Shevchenko) Date: Tue, 3 Aug 2021 13:33:38 +0300 Subject: [RFC PATCH 0/6] leds: Fix pca955x GPIO pin mappings In-Reply-To: <50aaf381-8cda-4656-9222-f23fda75d3bc@www.fastmail.com> References: <20210723075858.376378-1-andrew@aj.id.au> <6cc64039-f82a-4c1e-ad2c-16fad7aa3178@www.fastmail.com> <50aaf381-8cda-4656-9222-f23fda75d3bc@www.fastmail.com> Message-ID: On Tue, Aug 3, 2021 at 7:07 AM Andrew Jeffery wrote: > On Thu, 29 Jul 2021, at 17:10, Andy Shevchenko wrote: > > On Thu, Jul 29, 2021 at 3:39 AM Andrew Jeffery wrote: > > > On Wed, 28 Jul 2021, at 18:43, Andy Shevchenko wrote: > > > > On Wed, Jul 28, 2021 at 8:43 AM Andrew Jeffery wrote: > > > > > However, userspace would never have > > > > > got the results it expected with the existing driver implementation, so > > > > > I guess you could argue that no such (useful) userspace exists. Given > > > > > that, we could adopt the strategy of always defining a gpiochip > > > > > covering the whole pin space, and parts of the devicetree binding just > > > > > become redundant. > > > > > > > > I'm lost now. GPIO has its own userspace ABI, how does it work right > > > > now in application to this chip? > > > > > > As above, it "works" if the GPIOs specified in the devicetree are > > > contiguous from line 0. It's broken if they're not. > > > > So, "it never works" means there is no bug. Now, what we need is to > > keep the same enumeration scheme, but if you wish to be used half/half > > (or any other ratio), the driver should do like the above mentioned > > PWM, i.e. register entire space and depending on the requestor either > > proceed with a line or mark it as BUSY. > > > > Ideally, looking into what the chip can do, this should be indeed > > converted to some like pin control + PWM + LED + GPIO drivers. Then > > the function in pin mux configuration can show what exactly is enabled > > on the certain line(s). > > So just to clarify, you want both solutions here? > > 1. A gpiochip that covers the entire pin space > 2. A pinmux implementation that manages pin allocation to the different drivers > > In that case we can largely leave this series as is? We only need to > adjust how we configure the gpiochip by dropping the pin-mapping > implementation? Nope. It's far from what I think of. Re-reading again your cover letter it points out that pin mux per se does not exist in the hardware. In this case things become a bit too complicated, but we still may manage to handle them. Before I was thinking about this hierarchy 1. pinmux driver (which is actually the main driver here) 2. LED driver (using regmap API) 3. GPIO driver (via gpio-regmap) 4. PWM driver. Now what we need here is some kind of "virtual" pinmux. Do I understand correctly? To be clear: I do not like putting everything into one driver when the logical parts may be separated. -- With Best Regards, Andy Shevchenko From iwona.winiarska at intel.com Tue Aug 3 21:31:19 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:19 +0200 Subject: [PATCH v2 00/15] Introduce PECI subsystem Message-ID: <20210803113134.2262882-1-iwona.winiarska@intel.com> Hi Greg, This is a second round of patches introducing PECI subsystem. I don't think it is ready to be applied right away (we're still missing r-b's), but I hope we have chance to complete discussion in the 5.15 development cycle. I would appreciate if you could take a look. Note: All changes to arch/x86 are contained within patches 01-02, plus small Kconfig change adding "depends on PECI" to GENERIC_LIB_X86 Kconfig in patch 10. The Platform Environment Control Interface (PECI) is a communication interface between Intel processors and management controllers (e.g. Baseboard Management Controller, BMC). This series adds a PECI subsystem and introduces drivers which run in the Linux instance on the management controller (not the main Intel processor) and is intended to be used by the OpenBMC [1], a Linux distribution for BMC devices. The information exposed over PECI (like processor and DIMM temperature) refers to the Intel processor and can be consumed by daemons running on the BMC to, for example, display the processor temperature in its web interface. The PECI bus is collection of code that provides interface support between PECI devices (that actually represent processors) and PECI controllers (such as the "peci-aspeed" controller) that allow to access physical PECI interface. PECI devices are bound to PECI drivers that provides access to PECI services. This series introduces a generic "peci-cpu" driver that exposes hardware monitoring "cputemp" and "dimmtemp" using the auxiliary bus. Exposing "raw" PECI to userspace, either to write userspace drivers or for debug/testing purpose was left out of this series to encourage writing kernel drivers instead, but may be pursued in the future. Introducing PECI to upstream Linux was already attempted before [2]. Since it's been over a year since last revision, and the series changed quite a bit in the meantime, I've decided to start from v1. I would also like to give credit to everyone who helped me with different aspects of preliminary review: - Pierre-Louis Bossart, - Tony Luck, - Andy Shevchenko, - Dave Hansen. [1] https://github.com/openbmc/openbmc [2] https://lore.kernel.org/openbmc/20191211194624.2872-1-jae.hyun.yoo at linux.intel.com/ Changes v1 -> v2: Biggest changes when it comes to diffstat are locking in HWMON (I decided to clean things up a bit while adding it), switching to devres usage in more places and exposing sysfs interface in separate patch. * Moved extending X86 ARCHITECTURE MAINTAINERS earlier in series (Dan) * Removed "default n" for GENERIC_LIB_X86 (Dan) * Added vendor prefix for peci-aspeed specific properties (Rob) * Refactored PECI to use devres consistently (Dan) * Added missing sysfs documentation and excluded adding peci-sysfs to separate patch (Dan) * Used module_init() instead of subsys_init() for peci module initialization (Dan) * Removed redundant struct peci_device member (Dan) * Improved PECI Kconfig help (Randy/Dan) * Fixed/removed log messages (Dan, Guenter) * Refactored peci-cputemp and peci-dimmtemp and added missing locks (Guenter) * Removed unused dev_set_drvdata() in peci-cputemp and peci-dimmtemp (Guenter) * Fixed used types, names, fixed broken and added additional comments to peci-hwmon (Guenter, Zev) * Refactored peci-dimmtemp to not return -ETIMEDOUT (Guenter) * Added sanity check for min_peci_revision in peci-hwmon drivers (Zev) * Added assert for DIMM_NUMS_MAX and additional warning in peci-dimmtemp (Zev) * Fixed macro names in peci-aspeed (Zev) * Refactored peci-aspeed sanitizing properties to a single helper function (Zev) * Fixed peci_cpu_device_ids definition for Broadwell Xeon D (David) * Refactor peci_request to use a single allocation (Zev) * Used min_t() to improve code readability (Zev) * Added macro for PECI_RDENDPTCFG_MMIO_WR_LEN_BASE and fixed adev type array name to more descriptive (Zev) * Fixed peci-hwmon commit-msg and documentation (Zev) Thanks -Iwona Iwona Winiarska (13): x86/cpu: Move intel-family to arch-independent headers x86/cpu: Extract cpuid helpers to arch-independent dt-bindings: Add generic bindings for PECI dt-bindings: Add bindings for peci-aspeed ARM: dts: aspeed: Add PECI controller nodes peci: Add core infrastructure peci: Add device detection peci: Add sysfs interface for PECI bus peci: Add support for PECI device drivers peci: Add peci-cpu driver hwmon: peci: Add cputemp driver hwmon: peci: Add dimmtemp driver docs: Add PECI documentation Jae Hyun Yoo (2): peci: Add peci-aspeed controller driver docs: hwmon: Document PECI drivers Documentation/ABI/testing/sysfs-bus-peci | 16 + .../devicetree/bindings/peci/peci-aspeed.yaml | 109 ++++ .../bindings/peci/peci-controller.yaml | 33 + Documentation/hwmon/index.rst | 2 + Documentation/hwmon/peci-cputemp.rst | 90 +++ Documentation/hwmon/peci-dimmtemp.rst | 57 ++ Documentation/index.rst | 1 + Documentation/peci/index.rst | 16 + Documentation/peci/peci.rst | 48 ++ MAINTAINERS | 32 + arch/arm/boot/dts/aspeed-g4.dtsi | 14 + arch/arm/boot/dts/aspeed-g5.dtsi | 14 + arch/arm/boot/dts/aspeed-g6.dtsi | 14 + arch/x86/Kconfig | 1 + arch/x86/include/asm/cpu.h | 3 - arch/x86/include/asm/intel-family.h | 141 +--- arch/x86/include/asm/microcode.h | 2 +- arch/x86/kvm/cpuid.h | 3 +- arch/x86/lib/Makefile | 2 +- drivers/Kconfig | 3 + drivers/Makefile | 1 + drivers/edac/mce_amd.c | 3 +- drivers/hwmon/Kconfig | 2 + drivers/hwmon/Makefile | 1 + drivers/hwmon/peci/Kconfig | 31 + drivers/hwmon/peci/Makefile | 7 + drivers/hwmon/peci/common.h | 58 ++ drivers/hwmon/peci/cputemp.c | 591 +++++++++++++++++ drivers/hwmon/peci/dimmtemp.c | 614 ++++++++++++++++++ drivers/peci/Kconfig | 37 ++ drivers/peci/Makefile | 10 + drivers/peci/controller/Kconfig | 16 + drivers/peci/controller/Makefile | 3 + drivers/peci/controller/peci-aspeed.c | 445 +++++++++++++ drivers/peci/core.c | 238 +++++++ drivers/peci/cpu.c | 344 ++++++++++ drivers/peci/device.c | 221 +++++++ drivers/peci/internal.h | 137 ++++ drivers/peci/request.c | 477 ++++++++++++++ drivers/peci/sysfs.c | 82 +++ include/linux/peci-cpu.h | 38 ++ include/linux/peci.h | 110 ++++ include/linux/x86/cpu.h | 9 + include/linux/x86/intel-family.h | 146 +++++ lib/Kconfig | 4 + lib/Makefile | 2 + lib/x86/Makefile | 3 + {arch/x86/lib => lib/x86}/cpu.c | 2 +- 48 files changed, 4084 insertions(+), 149 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-peci create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml create mode 100644 Documentation/devicetree/bindings/peci/peci-controller.yaml create mode 100644 Documentation/hwmon/peci-cputemp.rst create mode 100644 Documentation/hwmon/peci-dimmtemp.rst create mode 100644 Documentation/peci/index.rst create mode 100644 Documentation/peci/peci.rst create mode 100644 drivers/hwmon/peci/Kconfig create mode 100644 drivers/hwmon/peci/Makefile create mode 100644 drivers/hwmon/peci/common.h create mode 100644 drivers/hwmon/peci/cputemp.c create mode 100644 drivers/hwmon/peci/dimmtemp.c create mode 100644 drivers/peci/Kconfig create mode 100644 drivers/peci/Makefile create mode 100644 drivers/peci/controller/Kconfig create mode 100644 drivers/peci/controller/Makefile create mode 100644 drivers/peci/controller/peci-aspeed.c create mode 100644 drivers/peci/core.c create mode 100644 drivers/peci/cpu.c create mode 100644 drivers/peci/device.c create mode 100644 drivers/peci/internal.h create mode 100644 drivers/peci/request.c create mode 100644 drivers/peci/sysfs.c create mode 100644 include/linux/peci-cpu.h create mode 100644 include/linux/peci.h create mode 100644 include/linux/x86/cpu.h create mode 100644 include/linux/x86/intel-family.h create mode 100644 lib/x86/Makefile rename {arch/x86/lib => lib/x86}/cpu.c (95%) -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:22 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:22 +0200 Subject: [PATCH v2 03/15] dt-bindings: Add generic bindings for PECI In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-4-iwona.winiarska@intel.com> Add device tree bindings for the PECI controller. Signed-off-by: Iwona Winiarska --- .../bindings/peci/peci-controller.yaml | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 Documentation/devicetree/bindings/peci/peci-controller.yaml diff --git a/Documentation/devicetree/bindings/peci/peci-controller.yaml b/Documentation/devicetree/bindings/peci/peci-controller.yaml new file mode 100644 index 000000000000..bbc3d3f3a929 --- /dev/null +++ b/Documentation/devicetree/bindings/peci/peci-controller.yaml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/peci/peci-controller.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Generic Device Tree Bindings for PECI + +maintainers: + - Iwona Winiarska + +description: + PECI (Platform Environment Control Interface) is an interface that provides a + communication channel from Intel processors and chipset components to external + monitoring or control devices. + +properties: + $nodename: + pattern: "^peci-controller(@.*)?$" + + cmd-timeout-ms: + description: + Command timeout in units of ms. + +additionalProperties: true + +examples: + - | + peci-controller at 1e78b000 { + reg = <0x1e78b000 0x100>; + cmd-timeout-ms = <500>; + }; +... -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:20 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:20 +0200 Subject: [PATCH v2 01/15] x86/cpu: Move intel-family to arch-independent headers In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-2-iwona.winiarska@intel.com> Baseboard management controllers (BMC) often run Linux but are usually implemented with non-X86 processors. They can use PECI to access package config space (PCS) registers on the host CPU and since some information, e.g. figuring out the core count, can be obtained using different registers on different CPU generations, they need to decode the family and model. Move the data from arch/x86/include/asm/intel-family.h into a new file include/linux/x86/intel-family.h so that it can be used by other architectures. Signed-off-by: Iwona Winiarska Reviewed-by: Tony Luck Reviewed-by: Dan Williams --- To limit tree-wide changes and help people that were expecting intel-family defines in arch/x86 to find it more easily without going through git history, we're not removing the original header completely, we're keeping it as a "stub" that includes the new one. If there is a consensus that the tree-wide option is better, we can choose this approach. MAINTAINERS | 2 + arch/x86/include/asm/intel-family.h | 141 +-------------------------- include/linux/x86/intel-family.h | 146 ++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 140 deletions(-) create mode 100644 include/linux/x86/intel-family.h diff --git a/MAINTAINERS b/MAINTAINERS index c9467d2839f5..104773d40952 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9241,6 +9241,7 @@ M: x86 at kernel.org L: linux-kernel at vger.kernel.org S: Supported F: arch/x86/include/asm/intel-family.h +F: include/linux/x86/intel-family.h INTEL DRM DRIVERS (excluding Poulsbo, Moorestown and derivative chipsets) M: Jani Nikula @@ -20105,6 +20106,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git x86/core F: Documentation/devicetree/bindings/x86/ F: Documentation/x86/ F: arch/x86/ +F: include/linux/x86/ X86 ENTRY CODE M: Andy Lutomirski diff --git a/arch/x86/include/asm/intel-family.h b/arch/x86/include/asm/intel-family.h index 27158436f322..0d4fe1b4e1f6 100644 --- a/arch/x86/include/asm/intel-family.h +++ b/arch/x86/include/asm/intel-family.h @@ -2,145 +2,6 @@ #ifndef _ASM_X86_INTEL_FAMILY_H #define _ASM_X86_INTEL_FAMILY_H -/* - * "Big Core" Processors (Branded as Core, Xeon, etc...) - * - * While adding a new CPUID for a new microarchitecture, add a new - * group to keep logically sorted out in chronological order. Within - * that group keep the CPUID for the variants sorted by model number. - * - * The defined symbol names have the following form: - * INTEL_FAM6{OPTFAMILY}_{MICROARCH}{OPTDIFF} - * where: - * OPTFAMILY Describes the family of CPUs that this belongs to. Default - * is assumed to be "_CORE" (and should be omitted). Other values - * currently in use are _ATOM and _XEON_PHI - * MICROARCH Is the code name for the micro-architecture for this core. - * N.B. Not the platform name. - * OPTDIFF If needed, a short string to differentiate by market segment. - * - * Common OPTDIFFs: - * - * - regular client parts - * _L - regular mobile parts - * _G - parts with extra graphics on - * _X - regular server parts - * _D - micro server parts - * - * Historical OPTDIFFs: - * - * _EP - 2 socket server parts - * _EX - 4+ socket server parts - * - * The #define line may optionally include a comment including platform or core - * names. An exception is made for skylake/kabylake where steppings seem to have gotten - * their own names :-( - */ - -/* Wildcard match for FAM6 so X86_MATCH_INTEL_FAM6_MODEL(ANY) works */ -#define INTEL_FAM6_ANY X86_MODEL_ANY - -#define INTEL_FAM6_CORE_YONAH 0x0E - -#define INTEL_FAM6_CORE2_MEROM 0x0F -#define INTEL_FAM6_CORE2_MEROM_L 0x16 -#define INTEL_FAM6_CORE2_PENRYN 0x17 -#define INTEL_FAM6_CORE2_DUNNINGTON 0x1D - -#define INTEL_FAM6_NEHALEM 0x1E -#define INTEL_FAM6_NEHALEM_G 0x1F /* Auburndale / Havendale */ -#define INTEL_FAM6_NEHALEM_EP 0x1A -#define INTEL_FAM6_NEHALEM_EX 0x2E - -#define INTEL_FAM6_WESTMERE 0x25 -#define INTEL_FAM6_WESTMERE_EP 0x2C -#define INTEL_FAM6_WESTMERE_EX 0x2F - -#define INTEL_FAM6_SANDYBRIDGE 0x2A -#define INTEL_FAM6_SANDYBRIDGE_X 0x2D -#define INTEL_FAM6_IVYBRIDGE 0x3A -#define INTEL_FAM6_IVYBRIDGE_X 0x3E - -#define INTEL_FAM6_HASWELL 0x3C -#define INTEL_FAM6_HASWELL_X 0x3F -#define INTEL_FAM6_HASWELL_L 0x45 -#define INTEL_FAM6_HASWELL_G 0x46 - -#define INTEL_FAM6_BROADWELL 0x3D -#define INTEL_FAM6_BROADWELL_G 0x47 -#define INTEL_FAM6_BROADWELL_X 0x4F -#define INTEL_FAM6_BROADWELL_D 0x56 - -#define INTEL_FAM6_SKYLAKE_L 0x4E /* Sky Lake */ -#define INTEL_FAM6_SKYLAKE 0x5E /* Sky Lake */ -#define INTEL_FAM6_SKYLAKE_X 0x55 /* Sky Lake */ -/* CASCADELAKE_X 0x55 Sky Lake -- s: 7 */ -/* COOPERLAKE_X 0x55 Sky Lake -- s: 11 */ - -#define INTEL_FAM6_KABYLAKE_L 0x8E /* Sky Lake */ -/* AMBERLAKE_L 0x8E Sky Lake -- s: 9 */ -/* COFFEELAKE_L 0x8E Sky Lake -- s: 10 */ -/* WHISKEYLAKE_L 0x8E Sky Lake -- s: 11,12 */ - -#define INTEL_FAM6_KABYLAKE 0x9E /* Sky Lake */ -/* COFFEELAKE 0x9E Sky Lake -- s: 10-13 */ - -#define INTEL_FAM6_COMETLAKE 0xA5 /* Sky Lake */ -#define INTEL_FAM6_COMETLAKE_L 0xA6 /* Sky Lake */ - -#define INTEL_FAM6_CANNONLAKE_L 0x66 /* Palm Cove */ - -#define INTEL_FAM6_ICELAKE_X 0x6A /* Sunny Cove */ -#define INTEL_FAM6_ICELAKE_D 0x6C /* Sunny Cove */ -#define INTEL_FAM6_ICELAKE 0x7D /* Sunny Cove */ -#define INTEL_FAM6_ICELAKE_L 0x7E /* Sunny Cove */ -#define INTEL_FAM6_ICELAKE_NNPI 0x9D /* Sunny Cove */ - -#define INTEL_FAM6_LAKEFIELD 0x8A /* Sunny Cove / Tremont */ - -#define INTEL_FAM6_ROCKETLAKE 0xA7 /* Cypress Cove */ - -#define INTEL_FAM6_TIGERLAKE_L 0x8C /* Willow Cove */ -#define INTEL_FAM6_TIGERLAKE 0x8D /* Willow Cove */ - -#define INTEL_FAM6_SAPPHIRERAPIDS_X 0x8F /* Golden Cove */ - -#define INTEL_FAM6_ALDERLAKE 0x97 /* Golden Cove / Gracemont */ -#define INTEL_FAM6_ALDERLAKE_L 0x9A /* Golden Cove / Gracemont */ - -/* "Small Core" Processors (Atom) */ - -#define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */ -#define INTEL_FAM6_ATOM_BONNELL_MID 0x26 /* Silverthorne, Lincroft */ - -#define INTEL_FAM6_ATOM_SALTWELL 0x36 /* Cedarview */ -#define INTEL_FAM6_ATOM_SALTWELL_MID 0x27 /* Penwell */ -#define INTEL_FAM6_ATOM_SALTWELL_TABLET 0x35 /* Cloverview */ - -#define INTEL_FAM6_ATOM_SILVERMONT 0x37 /* Bay Trail, Valleyview */ -#define INTEL_FAM6_ATOM_SILVERMONT_D 0x4D /* Avaton, Rangely */ -#define INTEL_FAM6_ATOM_SILVERMONT_MID 0x4A /* Merriefield */ - -#define INTEL_FAM6_ATOM_AIRMONT 0x4C /* Cherry Trail, Braswell */ -#define INTEL_FAM6_ATOM_AIRMONT_MID 0x5A /* Moorefield */ -#define INTEL_FAM6_ATOM_AIRMONT_NP 0x75 /* Lightning Mountain */ - -#define INTEL_FAM6_ATOM_GOLDMONT 0x5C /* Apollo Lake */ -#define INTEL_FAM6_ATOM_GOLDMONT_D 0x5F /* Denverton */ - -/* Note: the micro-architecture is "Goldmont Plus" */ -#define INTEL_FAM6_ATOM_GOLDMONT_PLUS 0x7A /* Gemini Lake */ - -#define INTEL_FAM6_ATOM_TREMONT_D 0x86 /* Jacobsville */ -#define INTEL_FAM6_ATOM_TREMONT 0x96 /* Elkhart Lake */ -#define INTEL_FAM6_ATOM_TREMONT_L 0x9C /* Jasper Lake */ - -/* Xeon Phi */ - -#define INTEL_FAM6_XEON_PHI_KNL 0x57 /* Knights Landing */ -#define INTEL_FAM6_XEON_PHI_KNM 0x85 /* Knights Mill */ - -/* Family 5 */ -#define INTEL_FAM5_QUARK_X1000 0x09 /* Quark X1000 SoC */ +#include #endif /* _ASM_X86_INTEL_FAMILY_H */ diff --git a/include/linux/x86/intel-family.h b/include/linux/x86/intel-family.h new file mode 100644 index 000000000000..ae4b075c1ab9 --- /dev/null +++ b/include/linux/x86/intel-family.h @@ -0,0 +1,146 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_X86_INTEL_FAMILY_H +#define _LINUX_X86_INTEL_FAMILY_H + +/* + * "Big Core" Processors (Branded as Core, Xeon, etc...) + * + * While adding a new CPUID for a new microarchitecture, add a new + * group to keep logically sorted out in chronological order. Within + * that group keep the CPUID for the variants sorted by model number. + * + * The defined symbol names have the following form: + * INTEL_FAM6{OPTFAMILY}_{MICROARCH}{OPTDIFF} + * where: + * OPTFAMILY Describes the family of CPUs that this belongs to. Default + * is assumed to be "_CORE" (and should be omitted). Other values + * currently in use are _ATOM and _XEON_PHI + * MICROARCH Is the code name for the micro-architecture for this core. + * N.B. Not the platform name. + * OPTDIFF If needed, a short string to differentiate by market segment. + * + * Common OPTDIFFs: + * + * - regular client parts + * _L - regular mobile parts + * _G - parts with extra graphics on + * _X - regular server parts + * _D - micro server parts + * + * Historical OPTDIFFs: + * + * _EP - 2 socket server parts + * _EX - 4+ socket server parts + * + * The #define line may optionally include a comment including platform or core + * names. An exception is made for skylake/kabylake where steppings seem to have gotten + * their own names :-( + */ + +/* Wildcard match for FAM6 so X86_MATCH_INTEL_FAM6_MODEL(ANY) works */ +#define INTEL_FAM6_ANY X86_MODEL_ANY + +#define INTEL_FAM6_CORE_YONAH 0x0E + +#define INTEL_FAM6_CORE2_MEROM 0x0F +#define INTEL_FAM6_CORE2_MEROM_L 0x16 +#define INTEL_FAM6_CORE2_PENRYN 0x17 +#define INTEL_FAM6_CORE2_DUNNINGTON 0x1D + +#define INTEL_FAM6_NEHALEM 0x1E +#define INTEL_FAM6_NEHALEM_G 0x1F /* Auburndale / Havendale */ +#define INTEL_FAM6_NEHALEM_EP 0x1A +#define INTEL_FAM6_NEHALEM_EX 0x2E + +#define INTEL_FAM6_WESTMERE 0x25 +#define INTEL_FAM6_WESTMERE_EP 0x2C +#define INTEL_FAM6_WESTMERE_EX 0x2F + +#define INTEL_FAM6_SANDYBRIDGE 0x2A +#define INTEL_FAM6_SANDYBRIDGE_X 0x2D +#define INTEL_FAM6_IVYBRIDGE 0x3A +#define INTEL_FAM6_IVYBRIDGE_X 0x3E + +#define INTEL_FAM6_HASWELL 0x3C +#define INTEL_FAM6_HASWELL_X 0x3F +#define INTEL_FAM6_HASWELL_L 0x45 +#define INTEL_FAM6_HASWELL_G 0x46 + +#define INTEL_FAM6_BROADWELL 0x3D +#define INTEL_FAM6_BROADWELL_G 0x47 +#define INTEL_FAM6_BROADWELL_X 0x4F +#define INTEL_FAM6_BROADWELL_D 0x56 + +#define INTEL_FAM6_SKYLAKE_L 0x4E /* Sky Lake */ +#define INTEL_FAM6_SKYLAKE 0x5E /* Sky Lake */ +#define INTEL_FAM6_SKYLAKE_X 0x55 /* Sky Lake */ +/* CASCADELAKE_X 0x55 Sky Lake -- s: 7 */ +/* COOPERLAKE_X 0x55 Sky Lake -- s: 11 */ + +#define INTEL_FAM6_KABYLAKE_L 0x8E /* Sky Lake */ +/* AMBERLAKE_L 0x8E Sky Lake -- s: 9 */ +/* COFFEELAKE_L 0x8E Sky Lake -- s: 10 */ +/* WHISKEYLAKE_L 0x8E Sky Lake -- s: 11,12 */ + +#define INTEL_FAM6_KABYLAKE 0x9E /* Sky Lake */ +/* COFFEELAKE 0x9E Sky Lake -- s: 10-13 */ + +#define INTEL_FAM6_COMETLAKE 0xA5 /* Sky Lake */ +#define INTEL_FAM6_COMETLAKE_L 0xA6 /* Sky Lake */ + +#define INTEL_FAM6_CANNONLAKE_L 0x66 /* Palm Cove */ + +#define INTEL_FAM6_ICELAKE_X 0x6A /* Sunny Cove */ +#define INTEL_FAM6_ICELAKE_D 0x6C /* Sunny Cove */ +#define INTEL_FAM6_ICELAKE 0x7D /* Sunny Cove */ +#define INTEL_FAM6_ICELAKE_L 0x7E /* Sunny Cove */ +#define INTEL_FAM6_ICELAKE_NNPI 0x9D /* Sunny Cove */ + +#define INTEL_FAM6_LAKEFIELD 0x8A /* Sunny Cove / Tremont */ + +#define INTEL_FAM6_ROCKETLAKE 0xA7 /* Cypress Cove */ + +#define INTEL_FAM6_TIGERLAKE_L 0x8C /* Willow Cove */ +#define INTEL_FAM6_TIGERLAKE 0x8D /* Willow Cove */ + +#define INTEL_FAM6_SAPPHIRERAPIDS_X 0x8F /* Golden Cove */ + +#define INTEL_FAM6_ALDERLAKE 0x97 /* Golden Cove / Gracemont */ +#define INTEL_FAM6_ALDERLAKE_L 0x9A /* Golden Cove / Gracemont */ + +/* "Small Core" Processors (Atom) */ + +#define INTEL_FAM6_ATOM_BONNELL 0x1C /* Diamondville, Pineview */ +#define INTEL_FAM6_ATOM_BONNELL_MID 0x26 /* Silverthorne, Lincroft */ + +#define INTEL_FAM6_ATOM_SALTWELL 0x36 /* Cedarview */ +#define INTEL_FAM6_ATOM_SALTWELL_MID 0x27 /* Penwell */ +#define INTEL_FAM6_ATOM_SALTWELL_TABLET 0x35 /* Cloverview */ + +#define INTEL_FAM6_ATOM_SILVERMONT 0x37 /* Bay Trail, Valleyview */ +#define INTEL_FAM6_ATOM_SILVERMONT_D 0x4D /* Avaton, Rangely */ +#define INTEL_FAM6_ATOM_SILVERMONT_MID 0x4A /* Merriefield */ + +#define INTEL_FAM6_ATOM_AIRMONT 0x4C /* Cherry Trail, Braswell */ +#define INTEL_FAM6_ATOM_AIRMONT_MID 0x5A /* Moorefield */ +#define INTEL_FAM6_ATOM_AIRMONT_NP 0x75 /* Lightning Mountain */ + +#define INTEL_FAM6_ATOM_GOLDMONT 0x5C /* Apollo Lake */ +#define INTEL_FAM6_ATOM_GOLDMONT_D 0x5F /* Denverton */ + +/* Note: the micro-architecture is "Goldmont Plus" */ +#define INTEL_FAM6_ATOM_GOLDMONT_PLUS 0x7A /* Gemini Lake */ + +#define INTEL_FAM6_ATOM_TREMONT_D 0x86 /* Jacobsville */ +#define INTEL_FAM6_ATOM_TREMONT 0x96 /* Elkhart Lake */ +#define INTEL_FAM6_ATOM_TREMONT_L 0x9C /* Jasper Lake */ + +/* Xeon Phi */ + +#define INTEL_FAM6_XEON_PHI_KNL 0x57 /* Knights Landing */ +#define INTEL_FAM6_XEON_PHI_KNM 0x85 /* Knights Mill */ + +/* Family 5 */ +#define INTEL_FAM5_QUARK_X1000 0x09 /* Quark X1000 SoC */ + +#endif /* _LINUX_X86_INTEL_FAMILY_H */ -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:21 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:21 +0200 Subject: [PATCH v2 02/15] x86/cpu: Extract cpuid helpers to arch-independent In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-3-iwona.winiarska@intel.com> Baseboard management controllers (BMC) often run Linux but are usually implemented with non-X86 processors. They can use PECI to access package config space (PCS) registers on the host CPU and since some information, e.g. figuring out the core count, can be obtained using different registers on different CPU generations, they need to decode the family and model. The format of Package Identifier PCS register that describes CPUID information has the same layout as CPUID_1.EAX, so let's allow to reuse cpuid helpers by making it available for other architectures as well. Signed-off-by: Iwona Winiarska Reviewed-by: Tony Luck Reviewed-by: Dan Williams --- MAINTAINERS | 1 + arch/x86/Kconfig | 1 + arch/x86/include/asm/cpu.h | 3 --- arch/x86/include/asm/microcode.h | 2 +- arch/x86/kvm/cpuid.h | 3 ++- arch/x86/lib/Makefile | 2 +- drivers/edac/mce_amd.c | 3 +-- include/linux/x86/cpu.h | 9 +++++++++ lib/Kconfig | 4 ++++ lib/Makefile | 2 ++ lib/x86/Makefile | 3 +++ {arch/x86/lib => lib/x86}/cpu.c | 2 +- 12 files changed, 26 insertions(+), 9 deletions(-) create mode 100644 include/linux/x86/cpu.h create mode 100644 lib/x86/Makefile rename {arch/x86/lib => lib/x86}/cpu.c (95%) diff --git a/MAINTAINERS b/MAINTAINERS index 104773d40952..7cdab7229651 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20107,6 +20107,7 @@ F: Documentation/devicetree/bindings/x86/ F: Documentation/x86/ F: arch/x86/ F: include/linux/x86/ +F: lib/x86/ X86 ENTRY CODE M: Andy Lutomirski diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 88fb922c23a0..9096593999ba 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -141,6 +141,7 @@ config X86 select GENERIC_IRQ_PROBE select GENERIC_IRQ_RESERVATION_MODE select GENERIC_IRQ_SHOW + select GENERIC_LIB_X86 select GENERIC_PENDING_IRQ if SMP select GENERIC_PTDUMP select GENERIC_SMP_IDLE_THREAD diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h index 33d41e350c79..2a663a05a795 100644 --- a/arch/x86/include/asm/cpu.h +++ b/arch/x86/include/asm/cpu.h @@ -37,9 +37,6 @@ extern int _debug_hotplug_cpu(int cpu, int action); int mwait_usable(const struct cpuinfo_x86 *); -unsigned int x86_family(unsigned int sig); -unsigned int x86_model(unsigned int sig); -unsigned int x86_stepping(unsigned int sig); #ifdef CONFIG_CPU_SUP_INTEL extern void __init sld_setup(struct cpuinfo_x86 *c); extern void switch_to_sld(unsigned long tifn); diff --git a/arch/x86/include/asm/microcode.h b/arch/x86/include/asm/microcode.h index ab45a220fac4..4b0eabf63b98 100644 --- a/arch/x86/include/asm/microcode.h +++ b/arch/x86/include/asm/microcode.h @@ -2,9 +2,9 @@ #ifndef _ASM_X86_MICROCODE_H #define _ASM_X86_MICROCODE_H -#include #include #include +#include struct ucode_patch { struct list_head plist; diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index c99edfff7f82..bf070d2a2175 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -4,10 +4,11 @@ #include "x86.h" #include "reverse_cpuid.h" -#include #include #include +#include + extern u32 kvm_cpu_caps[NR_KVM_CPU_CAPS] __read_mostly; void kvm_set_cpu_caps(void); diff --git a/arch/x86/lib/Makefile b/arch/x86/lib/Makefile index bad4dee4f0e4..fd73c1b72c3e 100644 --- a/arch/x86/lib/Makefile +++ b/arch/x86/lib/Makefile @@ -41,7 +41,7 @@ clean-files := inat-tables.c obj-$(CONFIG_SMP) += msr-smp.o cache-smp.o -lib-y := delay.o misc.o cmdline.o cpu.o +lib-y := delay.o misc.o cmdline.o lib-y += usercopy_$(BITS).o usercopy.o getuser.o putuser.o lib-y += memcpy_$(BITS).o lib-$(CONFIG_ARCH_HAS_COPY_MC) += copy_mc.o copy_mc_64.o diff --git a/drivers/edac/mce_amd.c b/drivers/edac/mce_amd.c index 27d56920b469..f545f5fad02c 100644 --- a/drivers/edac/mce_amd.c +++ b/drivers/edac/mce_amd.c @@ -1,8 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include - -#include +#include #include "mce_amd.h" diff --git a/include/linux/x86/cpu.h b/include/linux/x86/cpu.h new file mode 100644 index 000000000000..5f383d47886d --- /dev/null +++ b/include/linux/x86/cpu.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +#ifndef _LINUX_X86_CPU_H +#define _LINUX_X86_CPU_H + +unsigned int x86_family(unsigned int sig); +unsigned int x86_model(unsigned int sig); +unsigned int x86_stepping(unsigned int sig); + +#endif /* _LINUX_X86_CPU_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 5c9c0687f76d..e538d4d773bd 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -715,3 +715,7 @@ config PLDMFW config ASN1_ENCODER tristate + +config GENERIC_LIB_X86 + bool + depends on X86 diff --git a/lib/Makefile b/lib/Makefile index 5efd1b435a37..befbd9413432 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -360,3 +360,5 @@ obj-$(CONFIG_CMDLINE_KUNIT_TEST) += cmdline_kunit.o obj-$(CONFIG_SLUB_KUNIT_TEST) += slub_kunit.o obj-$(CONFIG_GENERIC_LIB_DEVMEM_IS_ALLOWED) += devmem_is_allowed.o + +obj-$(CONFIG_GENERIC_LIB_X86) += x86/ diff --git a/lib/x86/Makefile b/lib/x86/Makefile new file mode 100644 index 000000000000..342024c272fc --- /dev/null +++ b/lib/x86/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-y := cpu.o diff --git a/arch/x86/lib/cpu.c b/lib/x86/cpu.c similarity index 95% rename from arch/x86/lib/cpu.c rename to lib/x86/cpu.c index 7ad68917a51e..17af59a2fddf 100644 --- a/arch/x86/lib/cpu.c +++ b/lib/x86/cpu.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only #include #include -#include +#include unsigned int x86_family(unsigned int sig) { -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:23 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:23 +0200 Subject: [PATCH v2 04/15] dt-bindings: Add bindings for peci-aspeed In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-5-iwona.winiarska@intel.com> Add device tree bindings for the peci-aspeed controller driver. Co-developed-by: Jae Hyun Yoo Signed-off-by: Jae Hyun Yoo Signed-off-by: Iwona Winiarska --- .../devicetree/bindings/peci/peci-aspeed.yaml | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml diff --git a/Documentation/devicetree/bindings/peci/peci-aspeed.yaml b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml new file mode 100644 index 000000000000..2929d1e000d8 --- /dev/null +++ b/Documentation/devicetree/bindings/peci/peci-aspeed.yaml @@ -0,0 +1,109 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/peci/peci-aspeed.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Aspeed PECI Bus Device Tree Bindings + +maintainers: + - Iwona Winiarska + - Jae Hyun Yoo + +allOf: + - $ref: peci-controller.yaml# + +properties: + compatible: + enum: + - aspeed,ast2400-peci + - aspeed,ast2500-peci + - aspeed,ast2600-peci + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + description: + Clock source for PECI controller. Should reference the external + oscillator clock. + maxItems: 1 + + resets: + maxItems: 1 + + cmd-timeout-ms: + minimum: 1 + maximum: 1000 + default: 1000 + + aspeed,clock-divider: + description: + This value determines PECI controller internal clock dividing + rate. The divider will be calculated as 2 raised to the power of + the given value. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + default: 0 + + aspeed,msg-timing: + description: + Message timing negotiation period. This value will determine the period + of message timing negotiation to be issued by PECI controller. The unit + of the programmed value is four times of PECI clock period. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + default: 1 + + aspeed,addr-timing: + description: + Address timing negotiation period. This value will determine the period + of address timing negotiation to be issued by PECI controller. The unit + of the programmed value is four times of PECI clock period. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 255 + default: 1 + + aspeed,rd-sampling-point: + description: + Read sampling point selection. The whole period of a bit time will be + divided into 16 time frames. This value will determine the time frame + in which the controller will sample PECI signal for data read back. + Usually in the middle of a bit time is the best. + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 15 + default: 8 + +required: + - compatible + - reg + - interrupts + - clocks + - resets + +additionalProperties: false + +examples: + - | + #include + #include + peci-controller at 1e78b000 { + compatible = "aspeed,ast2600-peci"; + reg = <0x1e78b000 0x100>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>; + resets = <&syscon ASPEED_RESET_PECI>; + cmd-timeout-ms = <1000>; + aspeed,clock-divider = <0>; + aspeed,msg-timing = <1>; + aspeed,addr-timing = <1>; + aspeed,rd-sampling-point = <8>; + }; +... -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:24 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:24 +0200 Subject: [PATCH v2 05/15] ARM: dts: aspeed: Add PECI controller nodes In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-6-iwona.winiarska@intel.com> Add PECI controller nodes with all required information. Co-developed-by: Jae Hyun Yoo Signed-off-by: Jae Hyun Yoo Signed-off-by: Iwona Winiarska --- arch/arm/boot/dts/aspeed-g4.dtsi | 14 ++++++++++++++ arch/arm/boot/dts/aspeed-g5.dtsi | 14 ++++++++++++++ arch/arm/boot/dts/aspeed-g6.dtsi | 14 ++++++++++++++ 3 files changed, 42 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index c5aeb3cf3a09..87f07d7396d0 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -385,6 +385,20 @@ ibt: ibt at 140 { }; }; + peci0: peci-controller at 1e78b000 { + compatible = "aspeed,ast2400-peci"; + reg = <0x1e78b000 0x60>; + interrupts = <15>; + clocks = <&syscon ASPEED_CLK_GATE_REFCLK>; + resets = <&syscon ASPEED_RESET_PECI>; + cmd-timeout-ms = <1000>; + aspeed,clock-divider = <0>; + aspeed,msg-timing = <1>; + aspeed,addr-timing = <1>; + aspeed,rd-sampling-point = <8>; + status = "disabled"; + }; + uart2: serial at 1e78d000 { compatible = "ns16550a"; reg = <0x1e78d000 0x20>; diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index 329eaeef66fb..f54d1a9eba22 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -506,6 +506,20 @@ ibt: ibt at 140 { }; }; + peci0: peci-controller at 1e78b000 { + compatible = "aspeed,ast2500-peci"; + reg = <0x1e78b000 0x60>; + interrupts = <15>; + clocks = <&syscon ASPEED_CLK_GATE_REFCLK>; + resets = <&syscon ASPEED_RESET_PECI>; + cmd-timeout-ms = <1000>; + aspeed,clock-divider = <0>; + aspeed,msg-timing = <1>; + aspeed,addr-timing = <1>; + aspeed,rd-sampling-point = <8>; + status = "disabled"; + }; + uart2: serial at 1e78d000 { compatible = "ns16550a"; reg = <0x1e78d000 0x20>; diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index f96607b7b4e2..7fd9eaa02be4 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -459,6 +459,20 @@ wdt4: watchdog at 1e7850c0 { status = "disabled"; }; + peci0: peci-controller at 1e78b000 { + compatible = "aspeed,ast2600-peci"; + reg = <0x1e78b000 0x100>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_REF0CLK>; + resets = <&syscon ASPEED_RESET_PECI>; + cmd-timeout-ms = <1000>; + aspeed,clock-divider = <0>; + aspeed,msg-timing = <1>; + aspeed,addr-timing = <1>; + aspeed,rd-sampling-point = <8>; + status = "disabled"; + }; + lpc: lpc at 1e789000 { compatible = "aspeed,ast2600-lpc-v2", "simple-mfd", "syscon"; reg = <0x1e789000 0x1000>; -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:25 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:25 +0200 Subject: [PATCH v2 06/15] peci: Add core infrastructure In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-7-iwona.winiarska@intel.com> Intel processors provide access for various services designed to support processor and DRAM thermal management, platform manageability and processor interface tuning and diagnostics. Those services are available via the Platform Environment Control Interface (PECI) that provides a communication channel between the processor and the Baseboard Management Controller (BMC) or other platform management device. This change introduces PECI subsystem by adding the initial core module and API for controller drivers. Co-developed-by: Jason M Bills Signed-off-by: Jason M Bills Co-developed-by: Jae Hyun Yoo Signed-off-by: Jae Hyun Yoo Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- MAINTAINERS | 9 +++ drivers/Kconfig | 3 + drivers/Makefile | 1 + drivers/peci/Kconfig | 15 ++++ drivers/peci/Makefile | 5 ++ drivers/peci/core.c | 155 ++++++++++++++++++++++++++++++++++++++++ drivers/peci/internal.h | 16 +++++ include/linux/peci.h | 99 +++++++++++++++++++++++++ 8 files changed, 303 insertions(+) create mode 100644 drivers/peci/Kconfig create mode 100644 drivers/peci/Makefile create mode 100644 drivers/peci/core.c create mode 100644 drivers/peci/internal.h create mode 100644 include/linux/peci.h diff --git a/MAINTAINERS b/MAINTAINERS index 7cdab7229651..d411974aaa5e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14503,6 +14503,15 @@ L: platform-driver-x86 at vger.kernel.org S: Maintained F: drivers/platform/x86/peaq-wmi.c +PECI SUBSYSTEM +M: Iwona Winiarska +R: Jae Hyun Yoo +L: openbmc at lists.ozlabs.org (moderated for non-subscribers) +S: Supported +F: Documentation/devicetree/bindings/peci/ +F: drivers/peci/ +F: include/linux/peci.h + PENSANDO ETHERNET DRIVERS M: Shannon Nelson M: drivers at pensando.io diff --git a/drivers/Kconfig b/drivers/Kconfig index 8bad63417a50..f472b3d972b3 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -236,4 +236,7 @@ source "drivers/interconnect/Kconfig" source "drivers/counter/Kconfig" source "drivers/most/Kconfig" + +source "drivers/peci/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index 27c018bdf4de..8d96f0c3dde5 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -189,3 +189,4 @@ obj-$(CONFIG_GNSS) += gnss/ obj-$(CONFIG_INTERCONNECT) += interconnect/ obj-$(CONFIG_COUNTER) += counter/ obj-$(CONFIG_MOST) += most/ +obj-$(CONFIG_PECI) += peci/ diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig new file mode 100644 index 000000000000..71a4ad81225a --- /dev/null +++ b/drivers/peci/Kconfig @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0-only + +menuconfig PECI + tristate "PECI support" + help + The Platform Environment Control Interface (PECI) is an interface + that provides a communication channel to Intel processors and + chipset components from external monitoring or control devices. + + If you are building a Baseboard Management Controller (BMC) kernel + for Intel platform say Y here and also to the specific driver for + your adapter(s) below. If unsure say N. + + This support is also available as a module. If so, the module + will be called peci. diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile new file mode 100644 index 000000000000..e789a354e842 --- /dev/null +++ b/drivers/peci/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +# Core functionality +peci-y := core.o +obj-$(CONFIG_PECI) += peci.o diff --git a/drivers/peci/core.c b/drivers/peci/core.c new file mode 100644 index 000000000000..7b3938af0396 --- /dev/null +++ b/drivers/peci/core.c @@ -0,0 +1,155 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +static DEFINE_IDA(peci_controller_ida); + +static void peci_controller_dev_release(struct device *dev) +{ + struct peci_controller *controller = to_peci_controller(dev); + + pm_runtime_disable(&controller->dev); + + mutex_destroy(&controller->bus_lock); + ida_free(&peci_controller_ida, controller->id); + fwnode_handle_put(controller->dev.fwnode); + kfree(controller); +} + +struct device_type peci_controller_type = { + .release = peci_controller_dev_release, +}; + +static struct peci_controller *peci_controller_alloc(struct device *dev, + struct peci_controller_ops *ops) +{ + struct fwnode_handle *node = fwnode_handle_get(dev_fwnode(dev)); + struct peci_controller *controller; + int ret; + + if (!ops->xfer) + return ERR_PTR(-EINVAL); + + controller = kzalloc(sizeof(*controller), GFP_KERNEL); + if (!controller) + return ERR_PTR(-ENOMEM); + + ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL); + if (ret < 0) + goto err; + controller->id = ret; + + controller->ops = ops; + + controller->dev.parent = dev; + controller->dev.bus = &peci_bus_type; + controller->dev.type = &peci_controller_type; + controller->dev.fwnode = node; + controller->dev.of_node = to_of_node(node); + + device_initialize(&controller->dev); + + mutex_init(&controller->bus_lock); + + pm_runtime_no_callbacks(&controller->dev); + pm_suspend_ignore_children(&controller->dev, true); + pm_runtime_enable(&controller->dev); + + return controller; + +err: + kfree(controller); + return ERR_PTR(ret); +} + +static void unregister_controller(void *_controller) +{ + struct peci_controller *controller = _controller; + + device_unregister(&controller->dev); +} + +/** + * devm_peci_controller_add() - add PECI controller + * @dev: device for devm operations + * @ops: pointer to controller specific methods + * + * In final stage of its probe(), peci_controller driver calls + * devm_peci_controller_add() to register itself with the PECI bus. + * + * Return: Pointer to the newly allocated controller or ERR_PTR() in case of failure. + */ +struct peci_controller *devm_peci_controller_add(struct device *dev, + struct peci_controller_ops *ops) +{ + struct peci_controller *controller; + int ret; + + controller = peci_controller_alloc(dev, ops); + if (IS_ERR(controller)) + return controller; + + ret = dev_set_name(&controller->dev, "peci-%d", controller->id); + if (ret) + goto err; + + ret = device_add(&controller->dev); + if (ret) + goto err; + + ret = devm_add_action_or_reset(dev, unregister_controller, controller); + if (ret) + return ERR_PTR(ret); + + return controller; + +err: + put_device(&controller->dev); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI); + +struct bus_type peci_bus_type = { + .name = "peci", +}; + +static int __init peci_init(void) +{ + int ret; + + ret = bus_register(&peci_bus_type); + if (ret < 0) { + pr_err("failed to register PECI bus type!\n"); + return ret; + } + + return 0; +} +module_init(peci_init); + +static void __exit peci_exit(void) +{ + bus_unregister(&peci_bus_type); +} +module_exit(peci_exit); + +MODULE_AUTHOR("Jason M Bills "); +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_AUTHOR("Iwona Winiarska "); +MODULE_DESCRIPTION("PECI bus core module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h new file mode 100644 index 000000000000..918dea745a86 --- /dev/null +++ b/drivers/peci/internal.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2018-2021 Intel Corporation */ + +#ifndef __PECI_INTERNAL_H +#define __PECI_INTERNAL_H + +#include +#include + +struct peci_controller; + +extern struct bus_type peci_bus_type; + +extern struct device_type peci_controller_type; + +#endif /* __PECI_INTERNAL_H */ diff --git a/include/linux/peci.h b/include/linux/peci.h new file mode 100644 index 000000000000..26e0a4e73b50 --- /dev/null +++ b/include/linux/peci.h @@ -0,0 +1,99 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2018-2021 Intel Corporation */ + +#ifndef __LINUX_PECI_H +#define __LINUX_PECI_H + +#include +#include +#include +#include + +/* + * Currently we don't support any PECI command over 32 bytes. + */ +#define PECI_REQUEST_MAX_BUF_SIZE 32 + +struct peci_controller; +struct peci_request; + +/** + * struct peci_controller_ops - PECI controller specific methods + * @xfer: PECI transfer function + * + * PECI controllers may have different hardware interfaces - the drivers + * implementing PECI controllers can use this structure to abstract away those + * differences by exposing a common interface for PECI core. + */ +struct peci_controller_ops { + int (*xfer)(struct peci_controller *controller, u8 addr, struct peci_request *req); +}; + +/** + * struct peci_controller - PECI controller + * @dev: device object to register PECI controller to the device model + * @ops: pointer to device specific controller operations + * @bus_lock: lock used to protect multiple callers + * @id: PECI controller ID + * + * PECI controllers usually connect to their drivers using non-PECI bus, + * such as the platform bus. + * Each PECI controller can communicate with one or more PECI devices. + */ +struct peci_controller { + struct device dev; + struct peci_controller_ops *ops; + struct mutex bus_lock; /* held for the duration of xfer */ + u8 id; +}; + +struct peci_controller *devm_peci_controller_add(struct device *parent, + struct peci_controller_ops *ops); + +static inline struct peci_controller *to_peci_controller(void *d) +{ + return container_of(d, struct peci_controller, dev); +} + +/** + * struct peci_device - PECI device + * @dev: device object to register PECI device to the device model + * @controller: manages the bus segment hosting this PECI device + * @addr: address used on the PECI bus connected to the parent controller + * + * A peci_device identifies a single device (i.e. CPU) connected to a PECI bus. + * The behaviour exposed to the rest of the system is defined by the PECI driver + * managing the device. + */ +struct peci_device { + struct device dev; + u8 addr; +}; + +static inline struct peci_device *to_peci_device(struct device *d) +{ + return container_of(d, struct peci_device, dev); +} + +/** + * struct peci_request - PECI request + * @device: PECI device to which the request is sent + * @tx: TX buffer specific data + * @tx.buf: TX buffer + * @tx.len: transfer data length in bytes + * @rx: RX buffer specific data + * @rx.buf: RX buffer + * @rx.len: received data length in bytes + * + * A peci_request represents a request issued by PECI originator (TX) and + * a response received from PECI responder (RX). + */ +struct peci_request { + struct peci_device *device; + struct { + u8 buf[PECI_REQUEST_MAX_BUF_SIZE]; + u8 len; + } rx, tx; +}; + +#endif /* __LINUX_PECI_H */ -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:26 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:26 +0200 Subject: [PATCH v2 07/15] peci: Add peci-aspeed controller driver In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-8-iwona.winiarska@intel.com> From: Jae Hyun Yoo ASPEED AST24xx/AST25xx/AST26xx SoCs supports the PECI electrical interface (a.k.a PECI wire). Signed-off-by: Jae Hyun Yoo Co-developed-by: Iwona Winiarska Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- MAINTAINERS | 9 + drivers/peci/Kconfig | 6 + drivers/peci/Makefile | 3 + drivers/peci/controller/Kconfig | 16 + drivers/peci/controller/Makefile | 3 + drivers/peci/controller/peci-aspeed.c | 445 ++++++++++++++++++++++++++ 6 files changed, 482 insertions(+) create mode 100644 drivers/peci/controller/Kconfig create mode 100644 drivers/peci/controller/Makefile create mode 100644 drivers/peci/controller/peci-aspeed.c diff --git a/MAINTAINERS b/MAINTAINERS index d411974aaa5e..6e9d53ff68ab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2866,6 +2866,15 @@ S: Maintained F: Documentation/hwmon/asc7621.rst F: drivers/hwmon/asc7621.c +ASPEED PECI CONTROLLER +M: Iwona Winiarska +M: Jae Hyun Yoo +L: linux-aspeed at lists.ozlabs.org (moderated for non-subscribers) +L: openbmc at lists.ozlabs.org (moderated for non-subscribers) +S: Supported +F: Documentation/devicetree/bindings/peci/peci-aspeed.yaml +F: drivers/peci/controller/peci-aspeed.c + ASPEED PINCTRL DRIVERS M: Andrew Jeffery L: linux-aspeed at lists.ozlabs.org (moderated for non-subscribers) diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig index 71a4ad81225a..99279df97a78 100644 --- a/drivers/peci/Kconfig +++ b/drivers/peci/Kconfig @@ -13,3 +13,9 @@ menuconfig PECI This support is also available as a module. If so, the module will be called peci. + +if PECI + +source "drivers/peci/controller/Kconfig" + +endif # PECI diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile index e789a354e842..926d8df15cbd 100644 --- a/drivers/peci/Makefile +++ b/drivers/peci/Makefile @@ -3,3 +3,6 @@ # Core functionality peci-y := core.o obj-$(CONFIG_PECI) += peci.o + +# Hardware specific bus drivers +obj-y += controller/ diff --git a/drivers/peci/controller/Kconfig b/drivers/peci/controller/Kconfig new file mode 100644 index 000000000000..6d48df08db1c --- /dev/null +++ b/drivers/peci/controller/Kconfig @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config PECI_ASPEED + tristate "ASPEED PECI support" + depends on ARCH_ASPEED || COMPILE_TEST + depends on OF + depends on HAS_IOMEM + help + This option enables PECI controller driver for ASPEED AST2400, + AST2500 and AST2600 SoCs. + + Say Y here if your system runs on ASPEED SoC and you are using it + as BMC for Intel platform. + + This driver can also be built as a module. If so, the module will + be called peci-aspeed. diff --git a/drivers/peci/controller/Makefile b/drivers/peci/controller/Makefile new file mode 100644 index 000000000000..022c28ef1bf0 --- /dev/null +++ b/drivers/peci/controller/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0-only + +obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o diff --git a/drivers/peci/controller/peci-aspeed.c b/drivers/peci/controller/peci-aspeed.c new file mode 100644 index 000000000000..1d708c983749 --- /dev/null +++ b/drivers/peci/controller/peci-aspeed.c @@ -0,0 +1,445 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2012-2017 ASPEED Technology Inc. +// Copyright (c) 2018-2021 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* ASPEED PECI Registers */ +/* Control Register */ +#define ASPEED_PECI_CTRL 0x00 +#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16) +#define ASPEED_PECI_CTRL_RD_MODE_MASK GENMASK(13, 12) +#define ASPEED_PECI_CTRL_RD_MODE_DBG BIT(13) +#define ASPEED_PECI_CTRL_RD_MODE_COUNT BIT(12) +#define ASPEED_PECI_CTRL_CLK_SOURCE BIT(11) +#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8) +#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7) +#define ASPEED_PECI_CTRL_INVERT_IN BIT(6) +#define ASPEED_PECI_CTRL_BUS_CONTENTION_EN BIT(5) +#define ASPEED_PECI_CTRL_PECI_EN BIT(4) +#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0) + +/* Timing Negotiation Register */ +#define ASPEED_PECI_TIMING_NEGOTIATION 0x04 +#define ASPEED_PECI_T_NEGO_MSG_MASK GENMASK(15, 8) +#define ASPEED_PECI_T_NEGO_ADDR_MASK GENMASK(7, 0) + +/* Command Register */ +#define ASPEED_PECI_CMD 0x08 +#define ASPEED_PECI_CMD_PIN_MONITORING BIT(31) +#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24) +#define ASPEED_PECI_CMD_STS_ADDR_T_NEGO 0x3 +#define ASPEED_PECI_CMD_IDLE_MASK \ + (ASPEED_PECI_CMD_STS_MASK | ASPEED_PECI_CMD_PIN_MONITORING) +#define ASPEED_PECI_CMD_FIRE BIT(0) + +/* Read/Write Length Register */ +#define ASPEED_PECI_RW_LENGTH 0x0c +#define ASPEED_PECI_AW_FCS_EN BIT(31) +#define ASPEED_PECI_RD_LEN_MASK GENMASK(23, 16) +#define ASPEED_PECI_WR_LEN_MASK GENMASK(15, 8) +#define ASPEED_PECI_TARGET_ADDR_MASK GENMASK(7, 0) + +/* Expected FCS Data Register */ +#define ASPEED_PECI_EXPECTED_FCS 0x10 +#define ASPEED_PECI_EXPECTED_RD_FCS_MASK GENMASK(23, 16) +#define ASPEED_PECI_EXPECTED_AW_FCS_AUTO_MASK GENMASK(15, 8) +#define ASPEED_PECI_EXPECTED_WR_FCS_MASK GENMASK(7, 0) + +/* Captured FCS Data Register */ +#define ASPEED_PECI_CAPTURED_FCS 0x14 +#define ASPEED_PECI_CAPTURED_RD_FCS_MASK GENMASK(23, 16) +#define ASPEED_PECI_CAPTURED_WR_FCS_MASK GENMASK(7, 0) + +/* Interrupt Register */ +#define ASPEED_PECI_INT_CTRL 0x18 +#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30) +#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0 +#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1 +#define ASPEED_PECI_MESSAGE_NEGO 2 +#define ASPEED_PECI_INT_MASK GENMASK(4, 0) +#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4) +#define ASPEED_PECI_INT_BUS_CONTENTION BIT(3) +#define ASPEED_PECI_INT_WR_FCS_BAD BIT(2) +#define ASPEED_PECI_INT_WR_FCS_ABORT BIT(1) +#define ASPEED_PECI_INT_CMD_DONE BIT(0) + +/* Interrupt Status Register */ +#define ASPEED_PECI_INT_STS 0x1c +#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16) + /* bits[4..0]: Same bit fields in the 'Interrupt Register' */ + +/* Rx/Tx Data Buffer Registers */ +#define ASPEED_PECI_WR_DATA0 0x20 +#define ASPEED_PECI_WR_DATA1 0x24 +#define ASPEED_PECI_WR_DATA2 0x28 +#define ASPEED_PECI_WR_DATA3 0x2c +#define ASPEED_PECI_RD_DATA0 0x30 +#define ASPEED_PECI_RD_DATA1 0x34 +#define ASPEED_PECI_RD_DATA2 0x38 +#define ASPEED_PECI_RD_DATA3 0x3c +#define ASPEED_PECI_WR_DATA4 0x40 +#define ASPEED_PECI_WR_DATA5 0x44 +#define ASPEED_PECI_WR_DATA6 0x48 +#define ASPEED_PECI_WR_DATA7 0x4c +#define ASPEED_PECI_RD_DATA4 0x50 +#define ASPEED_PECI_RD_DATA5 0x54 +#define ASPEED_PECI_RD_DATA6 0x58 +#define ASPEED_PECI_RD_DATA7 0x5c +#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32 + +/* Timing Negotiation */ +#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8 +#define ASPEED_PECI_RD_SAMPLING_POINT_MAX (BIT(4) - 1) +#define ASPEED_PECI_CLK_DIV_DEFAULT 0 +#define ASPEED_PECI_CLK_DIV_MAX (BIT(3) - 1) +#define ASPEED_PECI_MSG_TIMING_DEFAULT 1 +#define ASPEED_PECI_MSG_TIMING_MAX (BIT(8) - 1) +#define ASPEED_PECI_ADDR_TIMING_DEFAULT 1 +#define ASPEED_PECI_ADDR_TIMING_MAX (BIT(8) - 1) + +/* Timeout */ +#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_US (50 * USEC_PER_MSEC) +#define ASPEED_PECI_IDLE_CHECK_INTERVAL_US (10 * USEC_PER_MSEC) +#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT (1000) +#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX (1000) + +struct aspeed_peci { + struct peci_controller *controller; + struct device *dev; + void __iomem *base; + struct clk *clk; + struct reset_control *rst; + int irq; + spinlock_t lock; /* to sync completion status handling */ + struct completion xfer_complete; + u32 status; + u32 cmd_timeout_ms; + u32 msg_timing; + u32 addr_timing; + u32 rd_sampling_point; + u32 clk_div; +}; + +static void aspeed_peci_init_regs(struct aspeed_peci *priv) +{ + u32 val; + + val = FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, ASPEED_PECI_CLK_DIV_DEFAULT); + val |= ASPEED_PECI_CTRL_PECI_CLK_EN; + writel(val, priv->base + ASPEED_PECI_CTRL); + /* + * Timing negotiation period setting. + * The unit of the programmed value is 4 times of PECI clock period. + */ + val = FIELD_PREP(ASPEED_PECI_T_NEGO_MSG_MASK, priv->msg_timing); + val |= FIELD_PREP(ASPEED_PECI_T_NEGO_ADDR_MASK, priv->addr_timing); + writel(val, priv->base + ASPEED_PECI_TIMING_NEGOTIATION); + + /* Clear interrupts */ + val = readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK; + writel(val, priv->base + ASPEED_PECI_INT_STS); + + /* Set timing negotiation mode and enable interrupts */ + val = FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO); + val |= ASPEED_PECI_INT_MASK; + writel(val, priv->base + ASPEED_PECI_INT_CTRL); + + val = FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, priv->rd_sampling_point); + val |= FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, priv->clk_div); + val |= ASPEED_PECI_CTRL_PECI_EN; + val |= ASPEED_PECI_CTRL_PECI_CLK_EN; + writel(val, priv->base + ASPEED_PECI_CTRL); +} + +static inline int aspeed_peci_check_idle(struct aspeed_peci *priv) +{ + u32 cmd_sts = readl(priv->base + ASPEED_PECI_CMD); + + if (FIELD_GET(ASPEED_PECI_CMD_STS_MASK, cmd_sts) == ASPEED_PECI_CMD_STS_ADDR_T_NEGO) + aspeed_peci_init_regs(priv); + + return readl_poll_timeout(priv->base + ASPEED_PECI_CMD, + cmd_sts, + !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK), + ASPEED_PECI_IDLE_CHECK_INTERVAL_US, + ASPEED_PECI_IDLE_CHECK_TIMEOUT_US); +} + +static int aspeed_peci_xfer(struct peci_controller *controller, + u8 addr, struct peci_request *req) +{ + struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent); + unsigned long flags, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); + u32 peci_head; + int ret; + + if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX || + req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX) + return -EINVAL; + + /* Check command sts and bus idle state */ + ret = aspeed_peci_check_idle(priv); + if (ret) + return ret; /* -ETIMEDOUT */ + + spin_lock_irqsave(&priv->lock, flags); + reinit_completion(&priv->xfer_complete); + + peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) | + FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) | + FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len); + + writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); + + memcpy_toio(priv->base + ASPEED_PECI_WR_DATA0, req->tx.buf, min_t(u8, req->tx.len, 16)); + if (req->tx.len > 16) + memcpy_toio(priv->base + ASPEED_PECI_WR_DATA4, req->tx.buf + 16, + req->tx.len - 16); + + dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); + print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req->tx.len); + + priv->status = 0; + writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); + spin_unlock_irqrestore(&priv->lock, flags); + + ret = wait_for_completion_interruptible_timeout(&priv->xfer_complete, timeout); + if (ret < 0) + return ret; + + if (ret == 0) { + dev_dbg(priv->dev, "Timeout waiting for a response!\n"); + return -ETIMEDOUT; + } + + spin_lock_irqsave(&priv->lock, flags); + + writel(0, priv->base + ASPEED_PECI_CMD); + + if (priv->status != ASPEED_PECI_INT_CMD_DONE) { + spin_unlock_irqrestore(&priv->lock, flags); + dev_dbg(priv->dev, "No valid response!\n"); + return -EIO; + } + + spin_unlock_irqrestore(&priv->lock, flags); + + memcpy_fromio(req->rx.buf, priv->base + ASPEED_PECI_RD_DATA0, min_t(u8, req->rx.len, 16)); + if (req->rx.len > 16) + memcpy_fromio(req->rx.buf + 16, priv->base + ASPEED_PECI_RD_DATA4, + req->rx.len - 16); + + print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req->rx.len); + + return 0; +} + +static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) +{ + struct aspeed_peci *priv = arg; + u32 status; + + spin_lock(&priv->lock); + status = readl(priv->base + ASPEED_PECI_INT_STS); + writel(status, priv->base + ASPEED_PECI_INT_STS); + priv->status |= (status & ASPEED_PECI_INT_MASK); + + /* + * In most cases, interrupt bits will be set one by one but also note + * that multiple interrupt bits could be set at the same time. + */ + if (status & ASPEED_PECI_INT_BUS_TIMEOUT) + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n"); + + if (status & ASPEED_PECI_INT_BUS_CONTENTION) + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_BUS_CONTENTION\n"); + + if (status & ASPEED_PECI_INT_WR_FCS_BAD) + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_WR_FCS_BAD\n"); + + if (status & ASPEED_PECI_INT_WR_FCS_ABORT) + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_WR_FCS_ABORT\n"); + + /* + * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit + * set even in an error case. + */ + if (status & ASPEED_PECI_INT_CMD_DONE) + complete(&priv->xfer_complete); + + spin_unlock(&priv->lock); + + return IRQ_HANDLED; +} + +static void aspeed_peci_property_sanitize(struct device *dev, const char *propname, + u32 min, u32 max, u32 default_val, u32 *propval) +{ + u32 val; + int ret; + + ret = device_property_read_u32(dev, propname, &val); + if (ret) { + val = default_val; + } else if (val > max || val < min) { + dev_warn(dev, "Invalid %s: %u, falling back to: %u\n", + propname, val, default_val); + + val = default_val; + } + + *propval = val; +} + +static void aspeed_peci_property_setup(struct aspeed_peci *priv) +{ + aspeed_peci_property_sanitize(priv->dev, "aspeed,clock-divider", + 0, ASPEED_PECI_CLK_DIV_MAX, + ASPEED_PECI_CLK_DIV_DEFAULT, &priv->clk_div); + aspeed_peci_property_sanitize(priv->dev, "aspeed,msg-timing", + 0, ASPEED_PECI_MSG_TIMING_MAX, + ASPEED_PECI_MSG_TIMING_DEFAULT, &priv->msg_timing); + aspeed_peci_property_sanitize(priv->dev, "aspeed,addr-timing", + 0, ASPEED_PECI_ADDR_TIMING_MAX, + ASPEED_PECI_ADDR_TIMING_DEFAULT, &priv->addr_timing); + aspeed_peci_property_sanitize(priv->dev, "aspeed,rd-sampling-point", + 0, ASPEED_PECI_RD_SAMPLING_POINT_MAX, + ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT, + &priv->rd_sampling_point); + aspeed_peci_property_sanitize(priv->dev, "cmd-timeout-ms", + 1, ASPEED_PECI_CMD_TIMEOUT_MS_MAX, + ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT, &priv->cmd_timeout_ms); +} + +static struct peci_controller_ops aspeed_ops = { + .xfer = aspeed_peci_xfer, +}; + +static void aspeed_peci_reset_control_release(void *data) +{ + reset_control_assert(data); +} + +int aspeed_peci_reset_control_deassert(struct device *dev, struct reset_control *rst) +{ + int ret; + + ret = reset_control_deassert(rst); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, aspeed_peci_reset_control_release, rst); +} + +static void aspeed_peci_clk_release(void *data) +{ + clk_disable_unprepare(data); +} + +static int aspeed_peci_clk_enable(struct device *dev, struct clk *clk) +{ + int ret; + + ret = clk_prepare_enable(clk); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, aspeed_peci_clk_release, clk); +} + +static int aspeed_peci_probe(struct platform_device *pdev) +{ + struct peci_controller *controller; + struct aspeed_peci *priv; + int ret; + + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = &pdev->dev; + dev_set_drvdata(priv->dev, priv); + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->irq = platform_get_irq(pdev, 0); + if (!priv->irq) + return priv->irq; + + ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler, + 0, "peci-aspeed", priv); + if (ret) + return ret; + + init_completion(&priv->xfer_complete); + spin_lock_init(&priv->lock); + + priv->rst = devm_reset_control_get(&pdev->dev, NULL); + if (IS_ERR(priv->rst)) + return dev_err_probe(priv->dev, PTR_ERR(priv->rst), + "failed to get reset control\n"); + + ret = aspeed_peci_reset_control_deassert(priv->dev, priv->rst); + if (ret) + return dev_err_probe(priv->dev, ret, "cannot deassert reset control\n"); + + priv->clk = devm_clk_get(priv->dev, NULL); + if (IS_ERR(priv->clk)) + return dev_err_probe(priv->dev, PTR_ERR(priv->clk), "failed to get clk\n"); + + ret = aspeed_peci_clk_enable(priv->dev, priv->clk); + if (ret) + return dev_err_probe(priv->dev, ret, "failed to enable clock\n"); + + aspeed_peci_property_setup(priv); + + aspeed_peci_init_regs(priv); + + controller = devm_peci_controller_add(priv->dev, &aspeed_ops); + if (IS_ERR(controller)) + return dev_err_probe(priv->dev, PTR_ERR(controller), + "failed to add aspeed peci controller\n"); + + priv->controller = controller; + + return 0; +} + +static const struct of_device_id aspeed_peci_of_table[] = { + { .compatible = "aspeed,ast2400-peci", }, + { .compatible = "aspeed,ast2500-peci", }, + { .compatible = "aspeed,ast2600-peci", }, + { } +}; +MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); + +static struct platform_driver aspeed_peci_driver = { + .probe = aspeed_peci_probe, + .driver = { + .name = "peci-aspeed", + .of_match_table = aspeed_peci_of_table, + }, +}; +module_platform_driver(aspeed_peci_driver); + +MODULE_AUTHOR("Ryan Chen "); +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_DESCRIPTION("ASPEED PECI driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI); -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:27 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:27 +0200 Subject: [PATCH v2 08/15] peci: Add device detection In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-9-iwona.winiarska@intel.com> Since PECI devices are discoverable, we can dynamically detect devices that are actually available in the system. This change complements the earlier implementation by rescanning PECI bus to detect available devices. For this purpose, it also introduces the minimal API for PECI requests. Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- drivers/peci/Makefile | 2 +- drivers/peci/core.c | 33 ++++++++++++ drivers/peci/device.c | 114 ++++++++++++++++++++++++++++++++++++++++ drivers/peci/internal.h | 14 +++++ drivers/peci/request.c | 50 ++++++++++++++++++ 5 files changed, 212 insertions(+), 1 deletion(-) create mode 100644 drivers/peci/device.c create mode 100644 drivers/peci/request.c diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile index 926d8df15cbd..c5f9d3fe21bb 100644 --- a/drivers/peci/Makefile +++ b/drivers/peci/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only # Core functionality -peci-y := core.o +peci-y := core.o request.o device.o obj-$(CONFIG_PECI) += peci.o # Hardware specific bus drivers diff --git a/drivers/peci/core.c b/drivers/peci/core.c index 7b3938af0396..d143f1a7fe98 100644 --- a/drivers/peci/core.c +++ b/drivers/peci/core.c @@ -34,6 +34,20 @@ struct device_type peci_controller_type = { .release = peci_controller_dev_release, }; +static int peci_controller_scan_devices(struct peci_controller *controller) +{ + int ret; + u8 addr; + + for (addr = PECI_BASE_ADDR; addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; addr++) { + ret = peci_device_create(controller, addr); + if (ret) + return ret; + } + + return 0; +} + static struct peci_controller *peci_controller_alloc(struct device *dev, struct peci_controller_ops *ops) { @@ -76,10 +90,23 @@ static struct peci_controller *peci_controller_alloc(struct device *dev, return ERR_PTR(ret); } +static int unregister_child(struct device *dev, void *dummy) +{ + peci_device_destroy(to_peci_device(dev)); + + return 0; +} + static void unregister_controller(void *_controller) { struct peci_controller *controller = _controller; + /* + * Detach any active PECI devices. This can't fail, thus we do not + * check the returned value. + */ + device_for_each_child_reverse(&controller->dev, NULL, unregister_child); + device_unregister(&controller->dev); } @@ -115,6 +142,12 @@ struct peci_controller *devm_peci_controller_add(struct device *dev, if (ret) return ERR_PTR(ret); + /* + * Ignoring retval since failures during scan are non-critical for + * controller itself. + */ + peci_controller_scan_devices(controller); + return controller; err: diff --git a/drivers/peci/device.c b/drivers/peci/device.c new file mode 100644 index 000000000000..32811248997b --- /dev/null +++ b/drivers/peci/device.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include +#include + +#include "internal.h" + +static int peci_detect(struct peci_controller *controller, u8 addr) +{ + struct peci_request *req; + int ret; + + /* + * PECI Ping is a command encoded by tx_len = 0, rx_len = 0. + * We expect correct Write FCS if the device at the target address + * is able to respond. + */ + req = peci_request_alloc(NULL, 0, 0); + if (!req) + return -ENOMEM; + + mutex_lock(&controller->bus_lock); + ret = controller->ops->xfer(controller, addr, req); + mutex_unlock(&controller->bus_lock); + + peci_request_free(req); + + return ret; +} + +static bool peci_addr_valid(u8 addr) +{ + return addr >= PECI_BASE_ADDR && addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; +} + +static int peci_dev_exists(struct device *dev, void *data) +{ + struct peci_device *device = to_peci_device(dev); + u8 *addr = data; + + if (device->addr == *addr) + return -EBUSY; + + return 0; +} + +int peci_device_create(struct peci_controller *controller, u8 addr) +{ + struct peci_device *device; + int ret; + + if (WARN_ON(!peci_addr_valid(addr))) + return -EINVAL; + + /* Check if we have already detected this device before. */ + ret = device_for_each_child(&controller->dev, &addr, peci_dev_exists); + if (ret) + return 0; + + ret = peci_detect(controller, addr); + if (ret) { + /* + * Device not present or host state doesn't allow successful + * detection at this time. + */ + if (ret == -EIO || ret == -ETIMEDOUT) + return 0; + + return ret; + } + + device = kzalloc(sizeof(*device), GFP_KERNEL); + if (!device) + return -ENOMEM; + + device->addr = addr; + device->dev.parent = &controller->dev; + device->dev.bus = &peci_bus_type; + device->dev.type = &peci_device_type; + + ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr); + if (ret) + goto err_free; + + ret = device_register(&device->dev); + if (ret) + goto err_put; + + return 0; + +err_put: + put_device(&device->dev); +err_free: + kfree(device); + + return ret; +} + +void peci_device_destroy(struct peci_device *device) +{ + device_unregister(&device->dev); +} + +static void peci_device_release(struct device *dev) +{ + struct peci_device *device = to_peci_device(dev); + + kfree(device); +} + +struct device_type peci_device_type = { + .release = peci_device_release, +}; diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h index 918dea745a86..57d11a902c5d 100644 --- a/drivers/peci/internal.h +++ b/drivers/peci/internal.h @@ -8,6 +8,20 @@ #include struct peci_controller; +struct peci_device; +struct peci_request; + +/* PECI CPU address range 0x30-0x37 */ +#define PECI_BASE_ADDR 0x30 +#define PECI_DEVICE_NUM_MAX 8 + +struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len); +void peci_request_free(struct peci_request *req); + +extern struct device_type peci_device_type; + +int peci_device_create(struct peci_controller *controller, u8 addr); +void peci_device_destroy(struct peci_device *device); extern struct bus_type peci_bus_type; diff --git a/drivers/peci/request.c b/drivers/peci/request.c new file mode 100644 index 000000000000..81b567bc7b87 --- /dev/null +++ b/drivers/peci/request.c @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021 Intel Corporation + +#include +#include +#include +#include + +#include "internal.h" + +/** + * peci_request_alloc() - allocate &struct peci_requests + * @device: PECI device to which request is going to be sent + * @tx_len: TX length + * @rx_len: RX length + * + * Return: A pointer to a newly allocated &struct peci_request on success or NULL otherwise. + */ +struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len) +{ + struct peci_request *req; + + if (WARN_ON_ONCE(tx_len > PECI_REQUEST_MAX_BUF_SIZE || rx_len > PECI_REQUEST_MAX_BUF_SIZE)) + return NULL; + /* + * PECI controllers that we are using now don't support DMA, this + * should be converted to DMA API once support for controllers that do + * allow it is added to avoid an extra copy. + */ + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + + req->device = device; + req->tx.len = tx_len; + req->rx.len = rx_len; + + return req; +} +EXPORT_SYMBOL_NS_GPL(peci_request_alloc, PECI); + +/** + * peci_request_free() - free peci_request + * @req: the PECI request to be freed + */ +void peci_request_free(struct peci_request *req) +{ + kfree(req); +} +EXPORT_SYMBOL_NS_GPL(peci_request_free, PECI); -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:28 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:28 +0200 Subject: [PATCH v2 09/15] peci: Add sysfs interface for PECI bus In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-10-iwona.winiarska@intel.com> PECI devices may not be discoverable at the time when PECI controller is being added (e.g. BMC can boot up when the Host system is still in S5). Since we currently don't have the capabilities to figure out the Host system state inside the PECI subsystem itself, we have to rely on userspace to do it for us. In the future, PECI subsystem may be expanded with mechanisms that allow us to avoid depending on userspace interaction (e.g. CPU presence could be detected using GPIO, and the information on whether it's discoverable could be obtained over IPMI). Unfortunately, those methods may ultimately not be available (support will vary from platform to platform), which means that we still need platform independent method triggered by userspace. Signed-off-by: Iwona Winiarska --- Documentation/ABI/testing/sysfs-bus-peci | 16 +++++ drivers/peci/Makefile | 2 +- drivers/peci/core.c | 3 +- drivers/peci/device.c | 1 + drivers/peci/internal.h | 5 ++ drivers/peci/sysfs.c | 82 ++++++++++++++++++++++++ 6 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-bus-peci create mode 100644 drivers/peci/sysfs.c diff --git a/Documentation/ABI/testing/sysfs-bus-peci b/Documentation/ABI/testing/sysfs-bus-peci new file mode 100644 index 000000000000..56c2b2216bbd --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-peci @@ -0,0 +1,16 @@ +What: /sys/bus/peci/rescan +Date: July 2021 +KernelVersion: 5.15 +Contact: Iwona Winiarska +Description: + Writing a non-zero value to this attribute will + initiate scan for PECI devices on all PECI controllers + in the system. + +What: /sys/bus/peci/devices/-/remove +Date: July 2021 +KernelVersion: 5.15 +Contact: Iwona Winiarska +Description: + Writing a non-zero value to this attribute will + remove the PECI device and any of its children. diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile index c5f9d3fe21bb..917f689e147a 100644 --- a/drivers/peci/Makefile +++ b/drivers/peci/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only # Core functionality -peci-y := core.o request.o device.o +peci-y := core.o request.o device.o sysfs.o obj-$(CONFIG_PECI) += peci.o # Hardware specific bus drivers diff --git a/drivers/peci/core.c b/drivers/peci/core.c index d143f1a7fe98..c473acb3c2a0 100644 --- a/drivers/peci/core.c +++ b/drivers/peci/core.c @@ -34,7 +34,7 @@ struct device_type peci_controller_type = { .release = peci_controller_dev_release, }; -static int peci_controller_scan_devices(struct peci_controller *controller) +int peci_controller_scan_devices(struct peci_controller *controller) { int ret; u8 addr; @@ -159,6 +159,7 @@ EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI); struct bus_type peci_bus_type = { .name = "peci", + .bus_groups = peci_bus_groups, }; static int __init peci_init(void) diff --git a/drivers/peci/device.c b/drivers/peci/device.c index 32811248997b..d77d9dabd51e 100644 --- a/drivers/peci/device.c +++ b/drivers/peci/device.c @@ -110,5 +110,6 @@ static void peci_device_release(struct device *dev) } struct device_type peci_device_type = { + .groups = peci_device_groups, .release = peci_device_release, }; diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h index 57d11a902c5d..978e12c8e1d3 100644 --- a/drivers/peci/internal.h +++ b/drivers/peci/internal.h @@ -8,6 +8,7 @@ #include struct peci_controller; +struct attribute_group; struct peci_device; struct peci_request; @@ -19,12 +20,16 @@ struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u void peci_request_free(struct peci_request *req); extern struct device_type peci_device_type; +extern const struct attribute_group *peci_device_groups[]; int peci_device_create(struct peci_controller *controller, u8 addr); void peci_device_destroy(struct peci_device *device); extern struct bus_type peci_bus_type; +extern const struct attribute_group *peci_bus_groups[]; extern struct device_type peci_controller_type; +int peci_controller_scan_devices(struct peci_controller *controller); + #endif /* __PECI_INTERNAL_H */ diff --git a/drivers/peci/sysfs.c b/drivers/peci/sysfs.c new file mode 100644 index 000000000000..db9ef05776e3 --- /dev/null +++ b/drivers/peci/sysfs.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021 Intel Corporation + +#include +#include +#include + +#include "internal.h" + +static int rescan_controller(struct device *dev, void *data) +{ + if (dev->type != &peci_controller_type) + return 0; + + return peci_controller_scan_devices(to_peci_controller(dev)); +} + +static ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count) +{ + bool res; + int ret; + + ret = kstrtobool(buf, &res); + if (ret) + return ret; + + if (!res) + return count; + + ret = bus_for_each_dev(&peci_bus_type, NULL, NULL, rescan_controller); + if (ret) + return ret; + + return count; +} +static BUS_ATTR_WO(rescan); + +static struct attribute *peci_bus_attrs[] = { + &bus_attr_rescan.attr, + NULL +}; + +static const struct attribute_group peci_bus_group = { + .attrs = peci_bus_attrs, +}; + +const struct attribute_group *peci_bus_groups[] = { + &peci_bus_group, + NULL +}; + +static ssize_t remove_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct peci_device *device = to_peci_device(dev); + bool res; + int ret; + + ret = kstrtobool(buf, &res); + if (ret) + return ret; + + if (res && device_remove_file_self(dev, attr)) + peci_device_destroy(device); + + return count; +} +static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, remove_store); + +static struct attribute *peci_device_attrs[] = { + &dev_attr_remove.attr, + NULL +}; + +static const struct attribute_group peci_device_group = { + .attrs = peci_device_attrs, +}; + +const struct attribute_group *peci_device_groups[] = { + &peci_device_group, + NULL +}; -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:29 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:29 +0200 Subject: [PATCH v2 10/15] peci: Add support for PECI device drivers In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-11-iwona.winiarska@intel.com> Here we're adding support for PECI device drivers, which unlike PECI controller drivers are actually able to provide functionalities to userspace. We're also extending peci_request API to allow querying more details about PECI device (e.g. model/family), that's going to be used to find a compatible peci_driver. Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- drivers/peci/Kconfig | 1 + drivers/peci/core.c | 49 +++++++++ drivers/peci/device.c | 105 ++++++++++++++++++++ drivers/peci/internal.h | 75 ++++++++++++++ drivers/peci/request.c | 214 ++++++++++++++++++++++++++++++++++++++++ include/linux/peci.h | 19 ++++ lib/Kconfig | 2 +- 7 files changed, 464 insertions(+), 1 deletion(-) diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig index 99279df97a78..1d0532e3a801 100644 --- a/drivers/peci/Kconfig +++ b/drivers/peci/Kconfig @@ -2,6 +2,7 @@ menuconfig PECI tristate "PECI support" + select GENERIC_LIB_X86 help The Platform Environment Control Interface (PECI) is an interface that provides a communication channel to Intel processors and diff --git a/drivers/peci/core.c b/drivers/peci/core.c index c473acb3c2a0..33c07920493d 100644 --- a/drivers/peci/core.c +++ b/drivers/peci/core.c @@ -157,8 +157,57 @@ struct peci_controller *devm_peci_controller_add(struct device *dev, } EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI); +static const struct peci_device_id * +peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device) +{ + while (id->family != 0) { + if (id->family == device->info.family && + id->model == device->info.model) + return id; + id++; + } + + return NULL; +} + +static int peci_bus_device_match(struct device *dev, struct device_driver *drv) +{ + struct peci_device *device = to_peci_device(dev); + struct peci_driver *peci_drv = to_peci_driver(drv); + + if (dev->type != &peci_device_type) + return 0; + + if (peci_bus_match_device_id(peci_drv->id_table, device)) + return 1; + + return 0; +} + +static int peci_bus_device_probe(struct device *dev) +{ + struct peci_device *device = to_peci_device(dev); + struct peci_driver *driver = to_peci_driver(dev->driver); + + return driver->probe(device, peci_bus_match_device_id(driver->id_table, device)); +} + +static int peci_bus_device_remove(struct device *dev) +{ + struct peci_device *device = to_peci_device(dev); + struct peci_driver *driver = to_peci_driver(dev->driver); + + if (driver->remove) + driver->remove(device); + + return 0; +} + struct bus_type peci_bus_type = { .name = "peci", + .match = peci_bus_device_match, + .probe = peci_bus_device_probe, + .remove = peci_bus_device_remove, .bus_groups = peci_bus_groups, }; diff --git a/drivers/peci/device.c b/drivers/peci/device.c index d77d9dabd51e..a78c02399574 100644 --- a/drivers/peci/device.c +++ b/drivers/peci/device.c @@ -1,11 +1,85 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2018-2021 Intel Corporation +#include #include #include +#include #include "internal.h" +#define REVISION_NUM_MASK GENMASK(15, 8) +static int peci_get_revision(struct peci_device *device, u8 *revision) +{ + struct peci_request *req; + u64 dib; + + req = peci_get_dib(device); + if (IS_ERR(req)) + return PTR_ERR(req); + + /* + * PECI device may be in a state where it is unable to return a proper + * DIB, in which case it returns 0 as DIB value. + * Let's treat this as an error to avoid carrying on with the detection + * using invalid revision. + */ + dib = peci_request_data_dib(req); + if (dib == 0) { + peci_request_free(req); + return -EIO; + } + + *revision = FIELD_GET(REVISION_NUM_MASK, dib); + + peci_request_free(req); + + return 0; +} + +static int peci_get_cpu_id(struct peci_device *device, u32 *cpu_id) +{ + struct peci_request *req; + int ret; + + req = peci_pkg_cfg_readl(device, PECI_PCS_PKG_ID, PECI_PKG_ID_CPU_ID); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *cpu_id = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} + +static int peci_device_info_init(struct peci_device *device) +{ + u8 revision; + u32 cpu_id; + int ret; + + ret = peci_get_cpu_id(device, &cpu_id); + if (ret) + return ret; + + device->info.family = x86_family(cpu_id); + device->info.model = x86_model(cpu_id); + + ret = peci_get_revision(device, &revision); + if (ret) + return ret; + device->info.peci_revision = revision; + + device->info.socket_id = device->addr - PECI_BASE_ADDR; + + return 0; +} + static int peci_detect(struct peci_controller *controller, u8 addr) { struct peci_request *req; @@ -79,6 +153,10 @@ int peci_device_create(struct peci_controller *controller, u8 addr) device->dev.bus = &peci_bus_type; device->dev.type = &peci_device_type; + ret = peci_device_info_init(device); + if (ret) + goto err_free; + ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr); if (ret) goto err_free; @@ -102,6 +180,33 @@ void peci_device_destroy(struct peci_device *device) device_unregister(&device->dev); } +int __peci_driver_register(struct peci_driver *driver, struct module *owner, + const char *mod_name) +{ + driver->driver.bus = &peci_bus_type; + driver->driver.owner = owner; + driver->driver.mod_name = mod_name; + + if (!driver->probe) { + pr_err("peci: trying to register driver without probe callback\n"); + return -EINVAL; + } + + if (!driver->id_table) { + pr_err("peci: trying to register driver without device id table\n"); + return -EINVAL; + } + + return driver_register(&driver->driver); +} +EXPORT_SYMBOL_NS_GPL(__peci_driver_register, PECI); + +void peci_driver_unregister(struct peci_driver *driver) +{ + driver_unregister(&driver->driver); +} +EXPORT_SYMBOL_NS_GPL(peci_driver_unregister, PECI); + static void peci_device_release(struct device *dev) { struct peci_device *device = to_peci_device(dev); diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h index 978e12c8e1d3..d661e1b65694 100644 --- a/drivers/peci/internal.h +++ b/drivers/peci/internal.h @@ -19,6 +19,34 @@ struct peci_request; struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len); void peci_request_free(struct peci_request *req); +int peci_request_status(struct peci_request *req); +u64 peci_request_data_dib(struct peci_request *req); + +u8 peci_request_data_readb(struct peci_request *req); +u16 peci_request_data_readw(struct peci_request *req); +u32 peci_request_data_readl(struct peci_request *req); +u64 peci_request_data_readq(struct peci_request *req); + +struct peci_request *peci_get_dib(struct peci_device *device); +struct peci_request *peci_get_temp(struct peci_device *device); + +struct peci_request *peci_pkg_cfg_readb(struct peci_device *device, u8 index, u16 param); +struct peci_request *peci_pkg_cfg_readw(struct peci_device *device, u8 index, u16 param); +struct peci_request *peci_pkg_cfg_readl(struct peci_device *device, u8 index, u16 param); +struct peci_request *peci_pkg_cfg_readq(struct peci_device *device, u8 index, u16 param); + +/** + * struct peci_device_id - PECI device data to match + * @data: pointer to driver private data specific to device + * @family: device family + * @model: device model + */ +struct peci_device_id { + const void *data; + u16 family; + u8 model; +}; + extern struct device_type peci_device_type; extern const struct attribute_group *peci_device_groups[]; @@ -28,6 +56,53 @@ void peci_device_destroy(struct peci_device *device); extern struct bus_type peci_bus_type; extern const struct attribute_group *peci_bus_groups[]; +/** + * struct peci_driver - PECI driver + * @driver: inherit device driver + * @probe: probe callback + * @remove: remove callback + * @id_table: PECI device match table to decide which device to bind + */ +struct peci_driver { + struct device_driver driver; + int (*probe)(struct peci_device *device, const struct peci_device_id *id); + void (*remove)(struct peci_device *device); + const struct peci_device_id *id_table; +}; + +static inline struct peci_driver *to_peci_driver(struct device_driver *d) +{ + return container_of(d, struct peci_driver, driver); +} + +int __peci_driver_register(struct peci_driver *driver, struct module *owner, + const char *mod_name); +/** + * peci_driver_register() - register PECI driver + * @driver: the driver to be registered + * @owner: owner module of the driver being registered + * @mod_name: module name string + * + * PECI drivers that don't need to do anything special in module init should + * use the convenience "module_peci_driver" macro instead + * + * Return: zero on success, else a negative error code. + */ +#define peci_driver_register(driver) \ + __peci_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) +void peci_driver_unregister(struct peci_driver *driver); + +/** + * module_peci_driver() - helper macro for registering a modular PECI driver + * @__peci_driver: peci_driver struct + * + * Helper macro for PECI drivers which do not do anything special in module + * init/exit. This eliminates a lot of boilerplate. Each module may only + * use this macro once, and calling it replaces module_init() and module_exit() + */ +#define module_peci_driver(__peci_driver) \ + module_driver(__peci_driver, peci_driver_register, peci_driver_unregister) + extern struct device_type peci_controller_type; int peci_controller_scan_devices(struct peci_controller *controller); diff --git a/drivers/peci/request.c b/drivers/peci/request.c index 81b567bc7b87..fe032d5a5e1b 100644 --- a/drivers/peci/request.c +++ b/drivers/peci/request.c @@ -1,13 +1,140 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (c) 2021 Intel Corporation +#include #include #include #include #include +#include + #include "internal.h" +#define PECI_GET_DIB_CMD 0xf7 +#define PECI_GET_DIB_WR_LEN 1 +#define PECI_GET_DIB_RD_LEN 8 + +#define PECI_RDPKGCFG_CMD 0xa1 +#define PECI_RDPKGCFG_WR_LEN 5 +#define PECI_RDPKGCFG_RD_LEN_BASE 1 +#define PECI_WRPKGCFG_CMD 0xa5 +#define PECI_WRPKGCFG_WR_LEN_BASE 6 +#define PECI_WRPKGCFG_RD_LEN 1 + +/* Device Specific Completion Code (CC) Definition */ +#define PECI_CC_SUCCESS 0x40 +#define PECI_CC_NEED_RETRY 0x80 +#define PECI_CC_OUT_OF_RESOURCE 0x81 +#define PECI_CC_UNAVAIL_RESOURCE 0x82 +#define PECI_CC_INVALID_REQ 0x90 +#define PECI_CC_MCA_ERROR 0x91 +#define PECI_CC_CATASTROPHIC_MCA_ERROR 0x93 +#define PECI_CC_FATAL_MCA_ERROR 0x94 +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB 0x98 +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR 0x9B +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA 0x9C + +#define PECI_RETRY_BIT BIT(0) + +#define PECI_RETRY_TIMEOUT msecs_to_jiffies(700) +#define PECI_RETRY_INTERVAL_MIN msecs_to_jiffies(1) +#define PECI_RETRY_INTERVAL_MAX msecs_to_jiffies(128) + +static u8 peci_request_data_cc(struct peci_request *req) +{ + return req->rx.buf[0]; +} + +/** + * peci_request_status() - return -errno based on PECI completion code + * @req: the PECI request that contains response data with completion code + * + * It can't be used for Ping(), GetDIB() and GetTemp() - for those commands we + * don't expect completion code in the response. + * + * Return: -errno + */ +int peci_request_status(struct peci_request *req) +{ + u8 cc = peci_request_data_cc(req); + + if (cc != PECI_CC_SUCCESS) + dev_dbg(&req->device->dev, "ret: %#02x\n", cc); + + switch (cc) { + case PECI_CC_SUCCESS: + return 0; + case PECI_CC_NEED_RETRY: + case PECI_CC_OUT_OF_RESOURCE: + case PECI_CC_UNAVAIL_RESOURCE: + return -EAGAIN; + case PECI_CC_INVALID_REQ: + return -EINVAL; + case PECI_CC_MCA_ERROR: + case PECI_CC_CATASTROPHIC_MCA_ERROR: + case PECI_CC_FATAL_MCA_ERROR: + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB: + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR: + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA: + return -EIO; + } + + WARN_ONCE(1, "Unknown PECI completion code: %#02x\n", cc); + + return -EIO; +} +EXPORT_SYMBOL_NS_GPL(peci_request_status, PECI); + +static int peci_request_xfer(struct peci_request *req) +{ + struct peci_device *device = req->device; + struct peci_controller *controller = to_peci_controller(device->dev.parent); + int ret; + + mutex_lock(&controller->bus_lock); + ret = controller->ops->xfer(controller, device->addr, req); + mutex_unlock(&controller->bus_lock); + + return ret; +} + +static int peci_request_xfer_retry(struct peci_request *req) +{ + long wait_interval = PECI_RETRY_INTERVAL_MIN; + struct peci_device *device = req->device; + struct peci_controller *controller = to_peci_controller(device->dev.parent); + unsigned long start = jiffies; + int ret; + + /* Don't try to use it for ping */ + if (WARN_ON(!req->rx.buf)) + return 0; + + do { + ret = peci_request_xfer(req); + if (ret) { + dev_dbg(&controller->dev, "xfer error: %d\n", ret); + return ret; + } + + if (peci_request_status(req) != -EAGAIN) + return 0; + + /* Set the retry bit to indicate a retry attempt */ + req->tx.buf[1] |= PECI_RETRY_BIT; + + if (schedule_timeout_interruptible(wait_interval)) + return -ERESTARTSYS; + + wait_interval = min_t(long, wait_interval * 2, PECI_RETRY_INTERVAL_MAX); + } while (time_before(jiffies, start + PECI_RETRY_TIMEOUT)); + + dev_dbg(&controller->dev, "request timed out\n"); + + return -ETIMEDOUT; +} + /** * peci_request_alloc() - allocate &struct peci_requests * @device: PECI device to which request is going to be sent @@ -48,3 +175,90 @@ void peci_request_free(struct peci_request *req) kfree(req); } EXPORT_SYMBOL_NS_GPL(peci_request_free, PECI); + +struct peci_request *peci_get_dib(struct peci_device *device) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_GET_DIB_CMD; + + ret = peci_request_xfer(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} +EXPORT_SYMBOL_NS_GPL(peci_get_dib, PECI); + +static struct peci_request * +__pkg_cfg_read(struct peci_device *device, u8 index, u16 param, u8 len) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, PECI_RDPKGCFG_WR_LEN, PECI_RDPKGCFG_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_RDPKGCFG_CMD; + req->tx.buf[1] = 0; + req->tx.buf[2] = index; + put_unaligned_le16(param, &req->tx.buf[3]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + +u8 peci_request_data_readb(struct peci_request *req) +{ + return req->rx.buf[1]; +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readb, PECI); + +u16 peci_request_data_readw(struct peci_request *req) +{ + return get_unaligned_le16(&req->rx.buf[1]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readw, PECI); + +u32 peci_request_data_readl(struct peci_request *req) +{ + return get_unaligned_le32(&req->rx.buf[1]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readl, PECI); + +u64 peci_request_data_readq(struct peci_request *req) +{ + return get_unaligned_le64(&req->rx.buf[1]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_readq, PECI); + +u64 peci_request_data_dib(struct peci_request *req) +{ + return get_unaligned_le64(&req->rx.buf[0]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_dib, PECI); + +#define __read_pkg_config(x, type) \ +struct peci_request *peci_pkg_cfg_##x(struct peci_device *device, u8 index, u16 param) \ +{ \ + return __pkg_cfg_read(device, index, param, sizeof(type)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_pkg_cfg_##x, PECI) + +__read_pkg_config(readb, u8); +__read_pkg_config(readw, u16); +__read_pkg_config(readl, u32); +__read_pkg_config(readq, u64); diff --git a/include/linux/peci.h b/include/linux/peci.h index 26e0a4e73b50..dcf1c53f4e40 100644 --- a/include/linux/peci.h +++ b/include/linux/peci.h @@ -14,6 +14,14 @@ */ #define PECI_REQUEST_MAX_BUF_SIZE 32 +#define PECI_PCS_PKG_ID 0 /* Package Identifier Read */ +#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ +#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ +#define PECI_PKG_ID_DEVICE_ID 0x0002 /* Uncore Device ID */ +#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ +#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ +#define PECI_PKG_ID_MCA_ERROR_LOG 0x0005 /* Machine Check Status */ + struct peci_controller; struct peci_request; @@ -59,6 +67,11 @@ static inline struct peci_controller *to_peci_controller(void *d) * struct peci_device - PECI device * @dev: device object to register PECI device to the device model * @controller: manages the bus segment hosting this PECI device + * @info: PECI device characteristics + * @info.family: device family + * @info.model: device model + * @info.peci_revision: PECI revision supported by the PECI device + * @info.socket_id: the socket ID represented by the PECI device * @addr: address used on the PECI bus connected to the parent controller * * A peci_device identifies a single device (i.e. CPU) connected to a PECI bus. @@ -67,6 +80,12 @@ static inline struct peci_controller *to_peci_controller(void *d) */ struct peci_device { struct device dev; + struct { + u16 family; + u8 model; + u8 peci_revision; + u8 socket_id; + } info; u8 addr; }; diff --git a/lib/Kconfig b/lib/Kconfig index e538d4d773bd..7f7972d357c2 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -718,4 +718,4 @@ config ASN1_ENCODER config GENERIC_LIB_X86 bool - depends on X86 + depends on X86 || PECI -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:32 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:32 +0200 Subject: [PATCH v2 13/15] hwmon: peci: Add dimmtemp driver In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-14-iwona.winiarska@intel.com> Add peci-dimmtemp driver for Temperature Sensor on DIMM readings that are accessible via the processor PECI interface. The main use case for the driver (and PECI interface) is out-of-band management, where we're able to obtain thermal readings from an external entity connected with PECI, e.g. BMC on server platforms. Co-developed-by: Jae Hyun Yoo Signed-off-by: Jae Hyun Yoo Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- Note that the timeout was completely removed - we're going to probe for detected DIMMs every 5 seconds until we reach "stable" state of either getting correct DIMM data or getting all -EINVAL (which suggest that the CPU doesn't have any DIMMs). drivers/hwmon/peci/Kconfig | 13 + drivers/hwmon/peci/Makefile | 2 + drivers/hwmon/peci/dimmtemp.c | 614 ++++++++++++++++++++++++++++++++++ 3 files changed, 629 insertions(+) create mode 100644 drivers/hwmon/peci/dimmtemp.c diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig index e10eed68d70a..9d32a57badfe 100644 --- a/drivers/hwmon/peci/Kconfig +++ b/drivers/hwmon/peci/Kconfig @@ -14,5 +14,18 @@ config SENSORS_PECI_CPUTEMP This driver can also be built as a module. If so, the module will be called peci-cputemp. +config SENSORS_PECI_DIMMTEMP + tristate "PECI DIMM temperature monitoring client" + depends on PECI + select SENSORS_PECI + select PECI_CPU + help + If you say yes here you get support for the generic Intel PECI hwmon + driver which provides Temperature Sensor on DIMM readings that are + accessible via the processor PECI interface. + + This driver can also be built as a module. If so, the module + will be called peci-dimmtemp. + config SENSORS_PECI tristate diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile index e8a0ada5ab1f..191cfa0227f3 100644 --- a/drivers/hwmon/peci/Makefile +++ b/drivers/hwmon/peci/Makefile @@ -1,5 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only peci-cputemp-y := cputemp.o +peci-dimmtemp-y := dimmtemp.o obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c new file mode 100644 index 000000000000..6264c29bb6c0 --- /dev/null +++ b/drivers/hwmon/peci/dimmtemp.c @@ -0,0 +1,614 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define DIMM_MASK_CHECK_DELAY_JIFFIES msecs_to_jiffies(5000) + +/* Max number of channel ranks and DIMM index per channel */ +#define CHAN_RANK_MAX_ON_HSX 8 +#define DIMM_IDX_MAX_ON_HSX 3 +#define CHAN_RANK_MAX_ON_BDX 4 +#define DIMM_IDX_MAX_ON_BDX 3 +#define CHAN_RANK_MAX_ON_BDXD 2 +#define DIMM_IDX_MAX_ON_BDXD 2 +#define CHAN_RANK_MAX_ON_SKX 6 +#define DIMM_IDX_MAX_ON_SKX 2 +#define CHAN_RANK_MAX_ON_ICX 8 +#define DIMM_IDX_MAX_ON_ICX 2 +#define CHAN_RANK_MAX_ON_ICXD 4 +#define DIMM_IDX_MAX_ON_ICXD 2 + +#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX +#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX +#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX) + +#define CPU_SEG_MASK GENMASK(23, 16) +#define GET_CPU_SEG(x) (((x) & CPU_SEG_MASK) >> 16) +#define CPU_BUS_MASK GENMASK(7, 0) +#define GET_CPU_BUS(x) ((x) & CPU_BUS_MASK) + +#define DIMM_TEMP_MAX GENMASK(15, 8) +#define DIMM_TEMP_CRIT GENMASK(23, 16) +#define GET_TEMP_MAX(x) (((x) & DIMM_TEMP_MAX) >> 8) +#define GET_TEMP_CRIT(x) (((x) & DIMM_TEMP_CRIT) >> 16) + +struct peci_dimmtemp; + +struct dimm_info { + int chan_rank_max; + int dimm_idx_max; + u8 min_peci_revision; + int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order, + int chan_rank, u32 *data); +}; + +struct peci_dimm_thresholds { + long temp_max; + long temp_crit; + struct peci_sensor_state state; +}; + +enum peci_dimm_threshold_type { + temp_max_type, + temp_crit_type, +}; + +struct peci_dimmtemp { + struct peci_device *peci_dev; + struct device *dev; + const char *name; + const struct dimm_info *gen_info; + struct delayed_work detect_work; + struct { + struct peci_sensor_data temp; + struct peci_dimm_thresholds thresholds; + } dimm[DIMM_NUMS_MAX]; + char **dimmtemp_label; + DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX); +}; + +static u8 __dimm_temp(u32 reg, int dimm_order) +{ + return (reg >> (dimm_order * 8)) & 0xff; +} + +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val) +{ + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + u32 data; + int ret; + + mutex_lock(&priv->dimm[dimm_no].temp.state.lock); + if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data); + if (ret) { + mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); + return ret; + } + + priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE; + + peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state); + +skip_update: + *val = priv->dimm[dimm_no].temp.value; + mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); + return 0; +} + +static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) +{ + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; + u32 data; + int ret; + + if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state)) + return 0; + + ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, &data); + if (ret == -ENODATA) /* Use default or previous value */ + return 0; + if (ret) + return ret; + + priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * MILLIDEGREE_PER_DEGREE; + priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * MILLIDEGREE_PER_DEGREE; + + peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state); + + return 0; +} + +static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_threshold_type type, + int dimm_no, long *val) +{ + int ret; + + mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); + ret = update_thresholds(priv, dimm_no); + if (ret) + goto unlock; + + switch (type) { + case temp_max_type: + *val = priv->dimm[dimm_no].thresholds.temp_max; + break; + case temp_crit_type: + *val = priv->dimm[dimm_no].thresholds.temp_crit; + break; + default: + ret = -EOPNOTSUPP; + break; + } +unlock: + mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); + + return ret; +} + +static int dimmtemp_read_string(struct device *dev, + enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct peci_dimmtemp *priv = dev_get_drvdata(dev); + + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = (const char *)priv->dimmtemp_label[channel]; + + return 0; +} + +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct peci_dimmtemp *priv = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + return get_dimm_temp(priv, channel, val); + case hwmon_temp_max: + return get_dimm_thresholds(priv, temp_max_type, channel, val); + case hwmon_temp_crit: + return get_dimm_thresholds(priv, temp_crit_type, channel, val); + default: + break; + } + + return -EOPNOTSUPP; +} + +static umode_t dimmtemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct peci_dimmtemp *priv = data; + + if (test_bit(channel, priv->dimm_mask)) + return 0444; + + return 0; +} + +static const struct hwmon_ops peci_dimmtemp_ops = { + .is_visible = dimmtemp_is_visible, + .read_string = dimmtemp_read_string, + .read = dimmtemp_read, +}; + +static int check_populated_dimms(struct peci_dimmtemp *priv) +{ + int chan_rank_max = priv->gen_info->chan_rank_max; + int dimm_idx_max = priv->gen_info->dimm_idx_max; + u32 chan_rank_empty = 0; + u64 dimm_mask = 0; + int chan_rank, dimm_idx, ret; + u32 pcs; + + BUILD_BUG_ON(CHAN_RANK_MAX > 32); + BUILD_BUG_ON(DIMM_NUMS_MAX > 64); + if (chan_rank_max * dimm_idx_max > DIMM_NUMS_MAX) { + WARN_ONCE(1, "Unsupported number of DIMMs"); + return -EINVAL; + } + + for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) { + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &pcs); + if (ret) { + /* + * Overall, we expect either success or -EINVAL in + * order to determine whether DIMM is populated or not. + * For anything else - we fall back to defering the + * detection to be performed at a later point in time. + */ + if (ret == -EINVAL) { + chan_rank_empty |= BIT(chan_rank); + continue; + } + + return -EAGAIN; + } + + for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++) + if (__dimm_temp(pcs, dimm_idx)) + dimm_mask |= BIT(chan_rank * dimm_idx_max + dimm_idx); + } + + /* If we got all -EINVALs, it means that the CPU doesn't have any DIMMs. */ + if (chan_rank_empty == GENMASK(chan_rank_max - 1, 0)) + return -ENODEV; + + /* + * It's possible that memory training is not done yet. In this case we + * defer the detection to be performed at a later point in time. + */ + if (!dimm_mask) + return -EAGAIN; + + dev_dbg(priv->dev, "Scanned populated DIMMs: %#llx\n", dimm_mask); + + bitmap_from_u64(priv->dimm_mask, dimm_mask); + + return 0; +} + +static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan) +{ + int rank = chan / priv->gen_info->dimm_idx_max; + int idx = chan % priv->gen_info->dimm_idx_max; + + priv->dimmtemp_label[chan] = devm_kasprintf(priv->dev, GFP_KERNEL, + "DIMM %c%d", 'A' + rank, + idx + 1); + if (!priv->dimmtemp_label[chan]) + return -ENOMEM; + + return 0; +} + +static const u32 peci_dimmtemp_temp_channel_config[] = { + [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT, + 0 +}; + +static const struct hwmon_channel_info peci_dimmtemp_temp_channel = { + .type = hwmon_temp, + .config = peci_dimmtemp_temp_channel_config, +}; + +static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = { + &peci_dimmtemp_temp_channel, + NULL +}; + +static const struct hwmon_chip_info peci_dimmtemp_chip_info = { + .ops = &peci_dimmtemp_ops, + .info = peci_dimmtemp_temp_info, +}; + +static int create_dimm_temp_info(struct peci_dimmtemp *priv) +{ + int ret, i, channels; + struct device *dev; + + /* + * We expect to either find populated DIMMs and carry on with creating + * sensors, or find out that there are no DIMMs populated. + * All other states mean that the platform never reached the state that + * allows to check DIMM state - causing us to retry later on. + */ + ret = check_populated_dimms(priv); + if (ret == -ENODEV) { + dev_dbg(priv->dev, "No DIMMs found\n"); + return 0; + } else if (ret) { + schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES); + dev_dbg(priv->dev, "Deferred populating DIMM temp info\n"); + return ret; + } + + channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max; + + priv->dimmtemp_label = devm_kzalloc(priv->dev, channels * sizeof(char *), GFP_KERNEL); + if (!priv->dimmtemp_label) + return -ENOMEM; + + for_each_set_bit(i, priv->dimm_mask, DIMM_NUMS_MAX) { + ret = create_dimm_temp_label(priv, i); + if (ret) + return ret; + mutex_init(&priv->dimm[i].thresholds.state.lock); + mutex_init(&priv->dimm[i].temp.state.lock); + } + + dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv, + &peci_dimmtemp_chip_info, NULL); + if (IS_ERR(dev)) { + dev_err(priv->dev, "Failed to register hwmon device\n"); + return PTR_ERR(dev); + } + + dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name); + + return 0; +} + +static void create_dimm_temp_info_delayed(struct work_struct *work) +{ + struct peci_dimmtemp *priv = container_of(to_delayed_work(work), + struct peci_dimmtemp, + detect_work); + int ret; + + ret = create_dimm_temp_info(priv); + if (ret && ret != -EAGAIN) + dev_err(priv->dev, "Failed to populate DIMM temp info\n"); +} + +static void remove_delayed_work(void *_priv) +{ + struct peci_dimmtemp *priv = _priv; + + cancel_delayed_work_sync(&priv->detect_work); +} + +static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct peci_device *peci_dev = to_peci_device(dev->parent); + struct peci_dimmtemp *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_dimmtemp.cpu%d", + peci_dev->info.socket_id); + if (!priv->name) + return -ENOMEM; + + priv->dev = dev; + priv->peci_dev = peci_dev; + priv->gen_info = (const struct dimm_info *)id->driver_data; + + /* + * This is just a sanity check. Since we're using commands that are + * guaranteed to be supported on a given platform, we should never see + * revision lower than expected. + */ + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) + dev_warn(priv->dev, + "Unexpected PECI revision %#x, some features may be unavailable\n", + peci_dev->info.peci_revision); + + INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed); + + ret = devm_add_action_or_reset(priv->dev, remove_delayed_work, priv); + if (ret) + return ret; + + ret = create_dimm_temp_info(priv); + if (ret && ret != -EAGAIN) { + dev_err(dev, "Failed to populate DIMM temp info\n"); + return ret; + } + + return 0; +} + +static int +read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 20, Function 0: IMC 0 channel 0 -> rank 0 + * Device 20, Function 1: IMC 0 channel 1 -> rank 1 + * Device 21, Function 0: IMC 0 channel 2 -> rank 2 + * Device 21, Function 1: IMC 0 channel 3 -> rank 3 + * Device 23, Function 0: IMC 1 channel 0 -> rank 4 + * Device 23, Function 1: IMC 1 channel 1 -> rank 5 + * Device 24, Function 0: IMC 1 channel 2 -> rank 6 + * Device 24, Function 1: IMC 1 channel 3 -> rank 7 + */ + dev = 20 + chan_rank / 2 + chan_rank / 4; + func = chan_rank % 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 1, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_bdxd(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 12, Function 2: IMC 1 channel 0 -> rank 2 + * Device 12, Function 6: IMC 1 channel 1 -> rank 3 + */ + dev = 10 + chan_rank / 2 * 2; + func = (chan_rank % 2) ? 6 : 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_skx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u8 dev, func; + u16 reg; + int ret; + + /* + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 + * Device 11, Function 2: IMC 0 channel 2 -> rank 2 + * Device 12, Function 2: IMC 1 channel 0 -> rank 3 + * Device 12, Function 6: IMC 1 channel 1 -> rank 4 + * Device 13, Function 2: IMC 1 channel 2 -> rank 5 + */ + dev = 10 + chan_rank / 3 * 2 + (chan_rank % 3 == 2 ? 1 : 0); + func = chan_rank % 3 == 1 ? 6 : 2; + reg = 0x120 + dimm_order * 4; + + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); + if (ret) + return ret; + + return 0; +} + +static int +read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) +{ + u32 reg_val; + u64 offset; + int ret; + u8 dev; + + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, ®_val); + if (ret || !(reg_val & BIT(31))) + return -ENODATA; /* Use default or previous value */ + + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, ®_val); + if (ret) + return -ENODATA; /* Use default or previous value */ + + /* + * Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0 + * Device 26, Offset 264e0: IMC 0 channel 1 -> rank 1 + * Device 27, Offset 224e0: IMC 1 channel 0 -> rank 2 + * Device 27, Offset 264e0: IMC 1 channel 1 -> rank 3 + * Device 28, Offset 224e0: IMC 2 channel 0 -> rank 4 + * Device 28, Offset 264e0: IMC 2 channel 1 -> rank 5 + * Device 29, Offset 224e0: IMC 3 channel 0 -> rank 6 + * Device 29, Offset 264e0: IMC 3 channel 1 -> rank 7 + */ + dev = 26 + chan_rank / 2; + offset = 0x224e0 + dimm_order * 4 + (chan_rank % 2) * 0x4000; + + ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val), + dev, 0, offset, data); + if (ret) + return ret; + + return 0; +} + +static const struct dimm_info dimm_hsx = { + .chan_rank_max = CHAN_RANK_MAX_ON_HSX, + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_hsx, +}; + +static const struct dimm_info dimm_bdx = { + .chan_rank_max = CHAN_RANK_MAX_ON_BDX, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_hsx, +}; + +static const struct dimm_info dimm_bdxd = { + .chan_rank_max = CHAN_RANK_MAX_ON_BDXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_BDXD, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_bdxd, +}; + +static const struct dimm_info dimm_skx = { + .chan_rank_max = CHAN_RANK_MAX_ON_SKX, + .dimm_idx_max = DIMM_IDX_MAX_ON_SKX, + .min_peci_revision = 0x33, + .read_thresholds = &read_thresholds_skx, +}; + +static const struct dimm_info dimm_icx = { + .chan_rank_max = CHAN_RANK_MAX_ON_ICX, + .dimm_idx_max = DIMM_IDX_MAX_ON_ICX, + .min_peci_revision = 0x40, + .read_thresholds = &read_thresholds_icx, +}; + +static const struct dimm_info dimm_icxd = { + .chan_rank_max = CHAN_RANK_MAX_ON_ICXD, + .dimm_idx_max = DIMM_IDX_MAX_ON_ICXD, + .min_peci_revision = 0x40, + .read_thresholds = &read_thresholds_icx, +}; + +static const struct auxiliary_device_id peci_dimmtemp_ids[] = { + { + .name = "peci_cpu.dimmtemp.hsx", + .driver_data = (kernel_ulong_t)&dimm_hsx, + }, + { + .name = "peci_cpu.dimmtemp.bdx", + .driver_data = (kernel_ulong_t)&dimm_bdx, + }, + { + .name = "peci_cpu.dimmtemp.bdxd", + .driver_data = (kernel_ulong_t)&dimm_bdxd, + }, + { + .name = "peci_cpu.dimmtemp.skx", + .driver_data = (kernel_ulong_t)&dimm_skx, + }, + { + .name = "peci_cpu.dimmtemp.icx", + .driver_data = (kernel_ulong_t)&dimm_icx, + }, + { + .name = "peci_cpu.dimmtemp.icxd", + .driver_data = (kernel_ulong_t)&dimm_icxd, + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids); + +static struct auxiliary_driver peci_dimmtemp_driver = { + .probe = peci_dimmtemp_probe, + .id_table = peci_dimmtemp_ids, +}; + +module_auxiliary_driver(peci_dimmtemp_driver); + +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_AUTHOR("Iwona Winiarska "); +MODULE_DESCRIPTION("PECI dimmtemp driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI_CPU); -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:30 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:30 +0200 Subject: [PATCH v2 11/15] peci: Add peci-cpu driver In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-12-iwona.winiarska@intel.com> PECI is an interface that may be used by different types of devices. Here we're adding a peci-cpu driver compatible with Intel processors. The driver is responsible for handling auxiliary devices that can subsequently be used by other drivers (e.g. hwmons). Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- MAINTAINERS | 1 + drivers/peci/Kconfig | 15 ++ drivers/peci/Makefile | 2 + drivers/peci/cpu.c | 344 +++++++++++++++++++++++++++++++++++++++ drivers/peci/device.c | 1 + drivers/peci/internal.h | 27 +++ drivers/peci/request.c | 213 ++++++++++++++++++++++++ include/linux/peci-cpu.h | 38 +++++ include/linux/peci.h | 8 - 9 files changed, 641 insertions(+), 8 deletions(-) create mode 100644 drivers/peci/cpu.c create mode 100644 include/linux/peci-cpu.h diff --git a/MAINTAINERS b/MAINTAINERS index 6e9d53ff68ab..3f5d48e1d143 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14519,6 +14519,7 @@ L: openbmc at lists.ozlabs.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/peci/ F: drivers/peci/ +F: include/linux/peci-cpu.h F: include/linux/peci.h PENSANDO ETHERNET DRIVERS diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig index 1d0532e3a801..2ea01f43f547 100644 --- a/drivers/peci/Kconfig +++ b/drivers/peci/Kconfig @@ -17,6 +17,21 @@ menuconfig PECI if PECI +config PECI_CPU + tristate "PECI CPU" + select AUXILIARY_BUS + help + This option enables peci-cpu driver for Intel processors. It is + responsible for creating auxiliary devices that can subsequently + be used by other drivers in order to perform various + functionalities such as e.g. temperature monitoring. + + Additional drivers must be enabled in order to use the functionality + of the device. + + This driver can also be built as a module. If so, the module + will be called peci-cpu. + source "drivers/peci/controller/Kconfig" endif # PECI diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile index 917f689e147a..7de18137e738 100644 --- a/drivers/peci/Makefile +++ b/drivers/peci/Makefile @@ -3,6 +3,8 @@ # Core functionality peci-y := core.o request.o device.o sysfs.o obj-$(CONFIG_PECI) += peci.o +peci-cpu-y := cpu.o +obj-$(CONFIG_PECI_CPU) += peci-cpu.o # Hardware specific bus drivers obj-y += controller/ diff --git a/drivers/peci/cpu.c b/drivers/peci/cpu.c new file mode 100644 index 000000000000..97b9043be1e2 --- /dev/null +++ b/drivers/peci/cpu.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2021 Intel Corporation + +#include +#include +#include +#include +#include +#include + +#include "internal.h" + +/** + * peci_temp_read() - read the maximum die temperature from PECI target device + * @device: PECI device to which request is going to be sent + * @temp_raw: where to store the read temperature + * + * It uses GetTemp PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_temp_read(struct peci_device *device, s16 *temp_raw) +{ + struct peci_request *req; + + req = peci_get_temp(device); + if (IS_ERR(req)) + return PTR_ERR(req); + + *temp_raw = peci_request_data_temp(req); + + peci_request_free(req); + + return 0; +} +EXPORT_SYMBOL_NS_GPL(peci_temp_read, PECI_CPU); + +/** + * peci_pcs_read() - read PCS register + * @device: PECI device to which request is going to be sent + * @index: PCS index + * @param: PCS parameter + * @data: where to store the read data + * + * It uses RdPkgConfig PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_pcs_read(struct peci_device *device, u8 index, u16 param, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_pkg_cfg_readl(device, index, param); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_pcs_read, PECI_CPU); + +/** + * peci_pci_local_read() - read 32-bit memory location using raw address + * @device: PECI device to which request is going to be sent + * @bus: bus + * @dev: device + * @func: function + * @reg: register + * @data: where to store the read data + * + * It uses RdPCIConfigLocal PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_pci_local_read(struct peci_device *device, u8 bus, u8 dev, u8 func, + u16 reg, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_pci_cfg_local_readl(device, bus, dev, func, reg); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_pci_local_read, PECI_CPU); + +/** + * peci_ep_pci_local_read() - read 32-bit memory location using raw address + * @device: PECI device to which request is going to be sent + * @seg: PCI segment + * @bus: bus + * @dev: device + * @func: function + * @reg: register + * @data: where to store the read data + * + * Like &peci_pci_local_read, but it uses RdEndpointConfig PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_ep_pci_local_read(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_ep_pci_cfg_local_readl(device, seg, bus, dev, func, reg); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_ep_pci_local_read, PECI_CPU); + +/** + * peci_mmio_read() - read 32-bit memory location using 64-bit bar offset address + * @device: PECI device to which request is going to be sent + * @bar: PCI bar + * @seg: PCI segment + * @bus: bus + * @dev: device + * @func: function + * @address: 64-bit MMIO address + * @data: where to store the read data + * + * It uses RdEndpointConfig PECI command. + * + * Return: 0 if succeeded, other values in case errors. + */ +int peci_mmio_read(struct peci_device *device, u8 bar, u8 seg, + u8 bus, u8 dev, u8 func, u64 address, u32 *data) +{ + struct peci_request *req; + int ret; + + req = peci_ep_mmio64_readl(device, bar, seg, bus, dev, func, address); + if (IS_ERR(req)) + return PTR_ERR(req); + + ret = peci_request_status(req); + if (ret) + goto out_req_free; + + *data = peci_request_data_readl(req); +out_req_free: + peci_request_free(req); + + return ret; +} +EXPORT_SYMBOL_NS_GPL(peci_mmio_read, PECI_CPU); + +static const char * const peci_adev_types[] = { + "cputemp", + "dimmtemp", +}; + +struct peci_cpu { + struct peci_device *device; + const struct peci_device_id *id; +}; + +static void adev_release(struct device *dev) +{ + struct auxiliary_device *adev = to_auxiliary_dev(dev); + + auxiliary_device_uninit(adev); + + kfree(adev->name); + kfree(adev); +} + +static struct auxiliary_device *adev_alloc(struct peci_cpu *priv, int idx) +{ + struct peci_controller *controller = to_peci_controller(priv->device->dev.parent); + struct auxiliary_device *adev; + const char *name; + int ret; + + adev = kzalloc(sizeof(*adev), GFP_KERNEL); + if (!adev) + return ERR_PTR(-ENOMEM); + + name = kasprintf(GFP_KERNEL, "%s.%s", peci_adev_types[idx], (const char *)priv->id->data); + if (!name) { + ret = -ENOMEM; + goto free_adev; + } + + adev->name = name; + adev->dev.parent = &priv->device->dev; + adev->dev.release = adev_release; + adev->id = (controller->id << 16) | (priv->device->addr); + + ret = auxiliary_device_init(adev); + if (ret) + goto free_name; + + return adev; + +free_name: + kfree(name); +free_adev: + kfree(adev); + return ERR_PTR(ret); +} + +static void unregister_adev(void *_adev) +{ + struct auxiliary_device *adev = _adev; + + auxiliary_device_delete(adev); +} + +static int devm_adev_add(struct device *dev, int idx) +{ + struct peci_cpu *priv = dev_get_drvdata(dev); + struct auxiliary_device *adev; + int ret; + + adev = adev_alloc(priv, idx); + if (IS_ERR(adev)) + return PTR_ERR(adev); + + ret = auxiliary_device_add(adev); + if (ret) { + auxiliary_device_uninit(adev); + return ret; + } + + ret = devm_add_action_or_reset(&priv->device->dev, unregister_adev, adev); + if (ret) + return ret; + + return 0; +} + +static void peci_cpu_add_adevices(struct peci_cpu *priv) +{ + struct device *dev = &priv->device->dev; + int ret, i; + + for (i = 0; i < ARRAY_SIZE(peci_adev_types); i++) { + ret = devm_adev_add(dev, i); + if (ret) { + dev_warn(dev, "Failed to register PECI auxiliary: %s, ret = %d\n", + peci_adev_types[i], ret); + continue; + } + } +} + +static int +peci_cpu_probe(struct peci_device *device, const struct peci_device_id *id) +{ + struct device *dev = &device->dev; + struct peci_cpu *priv; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + priv->device = device; + priv->id = id; + + peci_cpu_add_adevices(priv); + + return 0; +} + +static const struct peci_device_id peci_cpu_device_ids[] = { + { /* Haswell Xeon */ + .family = 6, + .model = INTEL_FAM6_HASWELL_X, + .data = "hsx", + }, + { /* Broadwell Xeon */ + .family = 6, + .model = INTEL_FAM6_BROADWELL_X, + .data = "bdx", + }, + { /* Broadwell Xeon D */ + .family = 6, + .model = INTEL_FAM6_BROADWELL_D, + .data = "bdxd", + }, + { /* Skylake Xeon */ + .family = 6, + .model = INTEL_FAM6_SKYLAKE_X, + .data = "skx", + }, + { /* Icelake Xeon */ + .family = 6, + .model = INTEL_FAM6_ICELAKE_X, + .data = "icx", + }, + { /* Icelake Xeon D */ + .family = 6, + .model = INTEL_FAM6_ICELAKE_D, + .data = "icxd", + }, + { } +}; +MODULE_DEVICE_TABLE(peci, peci_cpu_device_ids); + +static struct peci_driver peci_cpu_driver = { + .probe = peci_cpu_probe, + .id_table = peci_cpu_device_ids, + .driver = { + .name = "peci-cpu", + }, +}; +module_peci_driver(peci_cpu_driver); + +MODULE_AUTHOR("Iwona Winiarska "); +MODULE_DESCRIPTION("PECI CPU driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI); diff --git a/drivers/peci/device.c b/drivers/peci/device.c index a78c02399574..9c377d32cec4 100644 --- a/drivers/peci/device.c +++ b/drivers/peci/device.c @@ -3,6 +3,7 @@ #include #include +#include #include #include diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h index d661e1b65694..c6bbf18b66cb 100644 --- a/drivers/peci/internal.h +++ b/drivers/peci/internal.h @@ -21,6 +21,7 @@ void peci_request_free(struct peci_request *req); int peci_request_status(struct peci_request *req); u64 peci_request_data_dib(struct peci_request *req); +s16 peci_request_data_temp(struct peci_request *req); u8 peci_request_data_readb(struct peci_request *req); u16 peci_request_data_readw(struct peci_request *req); @@ -35,6 +36,32 @@ struct peci_request *peci_pkg_cfg_readw(struct peci_device *device, u8 index, u1 struct peci_request *peci_pkg_cfg_readl(struct peci_device *device, u8 index, u16 param); struct peci_request *peci_pkg_cfg_readq(struct peci_device *device, u8 index, u16 param); +struct peci_request *peci_pci_cfg_local_readb(struct peci_device *device, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_pci_cfg_local_readw(struct peci_device *device, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_pci_cfg_local_readl(struct peci_device *device, + u8 bus, u8 dev, u8 func, u16 reg); + +struct peci_request *peci_ep_pci_cfg_local_readb(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_ep_pci_cfg_local_readw(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_ep_pci_cfg_local_readl(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); + +struct peci_request *peci_ep_pci_cfg_readb(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_ep_pci_cfg_readw(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); +struct peci_request *peci_ep_pci_cfg_readl(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg); + +struct peci_request *peci_ep_mmio32_readl(struct peci_device *device, u8 bar, u8 seg, + u8 bus, u8 dev, u8 func, u64 offset); + +struct peci_request *peci_ep_mmio64_readl(struct peci_device *device, u8 bar, u8 seg, + u8 bus, u8 dev, u8 func, u64 offset); /** * struct peci_device_id - PECI device data to match * @data: pointer to driver private data specific to device diff --git a/drivers/peci/request.c b/drivers/peci/request.c index fe032d5a5e1b..eacd6d2513b1 100644 --- a/drivers/peci/request.c +++ b/drivers/peci/request.c @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -15,6 +16,10 @@ #define PECI_GET_DIB_WR_LEN 1 #define PECI_GET_DIB_RD_LEN 8 +#define PECI_GET_TEMP_CMD 0x01 +#define PECI_GET_TEMP_WR_LEN 1 +#define PECI_GET_TEMP_RD_LEN 2 + #define PECI_RDPKGCFG_CMD 0xa1 #define PECI_RDPKGCFG_WR_LEN 5 #define PECI_RDPKGCFG_RD_LEN_BASE 1 @@ -22,6 +27,45 @@ #define PECI_WRPKGCFG_WR_LEN_BASE 6 #define PECI_WRPKGCFG_RD_LEN 1 +#define PECI_RDIAMSR_CMD 0xb1 +#define PECI_RDIAMSR_WR_LEN 5 +#define PECI_RDIAMSR_RD_LEN 9 +#define PECI_WRIAMSR_CMD 0xb5 +#define PECI_RDIAMSREX_CMD 0xd1 +#define PECI_RDIAMSREX_WR_LEN 6 +#define PECI_RDIAMSREX_RD_LEN 9 + +#define PECI_RDPCICFG_CMD 0x61 +#define PECI_RDPCICFG_WR_LEN 6 +#define PECI_RDPCICFG_RD_LEN 5 +#define PECI_RDPCICFG_RD_LEN_MAX 24 +#define PECI_WRPCICFG_CMD 0x65 + +#define PECI_RDPCICFGLOCAL_CMD 0xe1 +#define PECI_RDPCICFGLOCAL_WR_LEN 5 +#define PECI_RDPCICFGLOCAL_RD_LEN_BASE 1 +#define PECI_WRPCICFGLOCAL_CMD 0xe5 +#define PECI_WRPCICFGLOCAL_WR_LEN_BASE 6 +#define PECI_WRPCICFGLOCAL_RD_LEN 1 + +#define PECI_ENDPTCFG_TYPE_LOCAL_PCI 0x03 +#define PECI_ENDPTCFG_TYPE_PCI 0x04 +#define PECI_ENDPTCFG_TYPE_MMIO 0x05 +#define PECI_ENDPTCFG_ADDR_TYPE_PCI 0x04 +#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_D 0x05 +#define PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q 0x06 +#define PECI_RDENDPTCFG_CMD 0xc1 +#define PECI_RDENDPTCFG_PCI_WR_LEN 12 +#define PECI_RDENDPTCFG_MMIO_WR_LEN_BASE 10 +#define PECI_RDENDPTCFG_MMIO_D_WR_LEN 14 +#define PECI_RDENDPTCFG_MMIO_Q_WR_LEN 18 +#define PECI_RDENDPTCFG_RD_LEN_BASE 1 +#define PECI_WRENDPTCFG_CMD 0xc5 +#define PECI_WRENDPTCFG_PCI_WR_LEN_BASE 13 +#define PECI_WRENDPTCFG_MMIO_D_WR_LEN_BASE 15 +#define PECI_WRENDPTCFG_MMIO_Q_WR_LEN_BASE 19 +#define PECI_WRENDPTCFG_RD_LEN 1 + /* Device Specific Completion Code (CC) Definition */ #define PECI_CC_SUCCESS 0x40 #define PECI_CC_NEED_RETRY 0x80 @@ -197,6 +241,27 @@ struct peci_request *peci_get_dib(struct peci_device *device) } EXPORT_SYMBOL_NS_GPL(peci_get_dib, PECI); +struct peci_request *peci_get_temp(struct peci_device *device) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, PECI_GET_TEMP_WR_LEN, PECI_GET_TEMP_RD_LEN); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_GET_TEMP_CMD; + + ret = peci_request_xfer(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} +EXPORT_SYMBOL_NS_GPL(peci_get_temp, PECI); + static struct peci_request * __pkg_cfg_read(struct peci_device *device, u8 index, u16 param, u8 len) { @@ -221,6 +286,108 @@ __pkg_cfg_read(struct peci_device *device, u8 index, u16 param, u8 len) return req; } +static u32 __get_pci_addr(u8 bus, u8 dev, u8 func, u16 reg) +{ + return reg | PCI_DEVID(bus, PCI_DEVFN(dev, func)) << 12; +} + +static struct peci_request * +__pci_cfg_local_read(struct peci_device *device, u8 bus, u8 dev, u8 func, u16 reg, u8 len) +{ + struct peci_request *req; + u32 pci_addr; + int ret; + + req = peci_request_alloc(device, PECI_RDPCICFGLOCAL_WR_LEN, + PECI_RDPCICFGLOCAL_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + pci_addr = __get_pci_addr(bus, dev, func, reg); + + req->tx.buf[0] = PECI_RDPCICFGLOCAL_CMD; + req->tx.buf[1] = 0; + put_unaligned_le24(pci_addr, &req->tx.buf[2]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + +static struct peci_request * +__ep_pci_cfg_read(struct peci_device *device, u8 msg_type, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg, u8 len) +{ + struct peci_request *req; + u32 pci_addr; + int ret; + + req = peci_request_alloc(device, PECI_RDENDPTCFG_PCI_WR_LEN, + PECI_RDENDPTCFG_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + pci_addr = __get_pci_addr(bus, dev, func, reg); + + req->tx.buf[0] = PECI_RDENDPTCFG_CMD; + req->tx.buf[1] = 0; + req->tx.buf[2] = msg_type; + req->tx.buf[3] = 0; + req->tx.buf[4] = 0; + req->tx.buf[5] = 0; + req->tx.buf[6] = PECI_ENDPTCFG_ADDR_TYPE_PCI; + req->tx.buf[7] = seg; /* PCI Segment */ + put_unaligned_le32(pci_addr, &req->tx.buf[8]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + +static struct peci_request * +__ep_mmio_read(struct peci_device *device, u8 bar, u8 addr_type, u8 seg, + u8 bus, u8 dev, u8 func, u64 offset, u8 tx_len, u8 len) +{ + struct peci_request *req; + int ret; + + req = peci_request_alloc(device, tx_len, PECI_RDENDPTCFG_RD_LEN_BASE + len); + if (!req) + return ERR_PTR(-ENOMEM); + + req->tx.buf[0] = PECI_RDENDPTCFG_CMD; + req->tx.buf[1] = 0; + req->tx.buf[2] = PECI_ENDPTCFG_TYPE_MMIO; + req->tx.buf[3] = 0; /* Endpoint ID */ + req->tx.buf[4] = 0; /* Reserved */ + req->tx.buf[5] = bar; + req->tx.buf[6] = addr_type; + req->tx.buf[7] = seg; /* PCI Segment */ + req->tx.buf[8] = PCI_DEVFN(dev, func); + req->tx.buf[9] = bus; /* PCI Bus */ + + if (addr_type == PECI_ENDPTCFG_ADDR_TYPE_MMIO_D) + put_unaligned_le32(offset, &req->tx.buf[10]); + else + put_unaligned_le64(offset, &req->tx.buf[10]); + + ret = peci_request_xfer_retry(req); + if (ret) { + peci_request_free(req); + return ERR_PTR(ret); + } + + return req; +} + u8 peci_request_data_readb(struct peci_request *req) { return req->rx.buf[1]; @@ -251,6 +418,12 @@ u64 peci_request_data_dib(struct peci_request *req) } EXPORT_SYMBOL_NS_GPL(peci_request_data_dib, PECI); +s16 peci_request_data_temp(struct peci_request *req) +{ + return get_unaligned_le16(&req->rx.buf[0]); +} +EXPORT_SYMBOL_NS_GPL(peci_request_data_temp, PECI); + #define __read_pkg_config(x, type) \ struct peci_request *peci_pkg_cfg_##x(struct peci_device *device, u8 index, u16 param) \ { \ @@ -262,3 +435,43 @@ __read_pkg_config(readb, u8); __read_pkg_config(readw, u16); __read_pkg_config(readl, u32); __read_pkg_config(readq, u64); + +#define __read_pci_config_local(x, type) \ +struct peci_request * \ +peci_pci_cfg_local_##x(struct peci_device *device, u8 bus, u8 dev, u8 func, u16 reg) \ +{ \ + return __pci_cfg_local_read(device, bus, dev, func, reg, sizeof(type)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_pci_cfg_local_##x, PECI) + +__read_pci_config_local(readb, u8); +__read_pci_config_local(readw, u16); +__read_pci_config_local(readl, u32); + +#define __read_ep_pci_config(x, msg_type, type) \ +struct peci_request * \ +peci_ep_pci_cfg_##x(struct peci_device *device, u8 seg, u8 bus, u8 dev, u8 func, u16 reg) \ +{ \ + return __ep_pci_cfg_read(device, msg_type, seg, bus, dev, func, reg, sizeof(type)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_ep_pci_cfg_##x, PECI) + +__read_ep_pci_config(local_readb, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u8); +__read_ep_pci_config(local_readw, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u16); +__read_ep_pci_config(local_readl, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u32); +__read_ep_pci_config(readb, PECI_ENDPTCFG_TYPE_PCI, u8); +__read_ep_pci_config(readw, PECI_ENDPTCFG_TYPE_PCI, u16); +__read_ep_pci_config(readl, PECI_ENDPTCFG_TYPE_PCI, u32); + +#define __read_ep_mmio(x, y, addr_type, type1, type2) \ +struct peci_request *peci_ep_mmio##y##_##x(struct peci_device *device, u8 bar, u8 seg, \ + u8 bus, u8 dev, u8 func, u64 offset) \ +{ \ + return __ep_mmio_read(device, bar, addr_type, seg, bus, dev, func, \ + offset, PECI_RDENDPTCFG_MMIO_WR_LEN_BASE + sizeof(type1), \ + sizeof(type2)); \ +} \ +EXPORT_SYMBOL_NS_GPL(peci_ep_mmio##y##_##x, PECI) + +__read_ep_mmio(readl, 32, PECI_ENDPTCFG_ADDR_TYPE_MMIO_D, u32, u32); +__read_ep_mmio(readl, 64, PECI_ENDPTCFG_ADDR_TYPE_MMIO_Q, u64, u32); diff --git a/include/linux/peci-cpu.h b/include/linux/peci-cpu.h new file mode 100644 index 000000000000..d1b307ec2429 --- /dev/null +++ b/include/linux/peci-cpu.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2021 Intel Corporation */ + +#ifndef __LINUX_PECI_CPU_H +#define __LINUX_PECI_CPU_H + +#include + +#define PECI_PCS_PKG_ID 0 /* Package Identifier Read */ +#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ +#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ +#define PECI_PKG_ID_DEVICE_ID 0x0002 /* Uncore Device ID */ +#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ +#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ +#define PECI_PKG_ID_MCA_ERROR_LOG 0x0005 /* Machine Check Status */ +#define PECI_PCS_MODULE_TEMP 9 /* Per Core DTS Temperature Read */ +#define PECI_PCS_THERMAL_MARGIN 10 /* DTS thermal margin */ +#define PECI_PCS_DDR_DIMM_TEMP 14 /* DDR DIMM Temperature */ +#define PECI_PCS_TEMP_TARGET 16 /* Temperature Target Read */ +#define PECI_PCS_TDP_UNITS 30 /* Units for power/energy registers */ + +struct peci_device; + +int peci_temp_read(struct peci_device *device, s16 *temp_raw); + +int peci_pcs_read(struct peci_device *device, u8 index, + u16 param, u32 *data); + +int peci_pci_local_read(struct peci_device *device, u8 bus, u8 dev, + u8 func, u16 reg, u32 *data); + +int peci_ep_pci_local_read(struct peci_device *device, u8 seg, + u8 bus, u8 dev, u8 func, u16 reg, u32 *data); + +int peci_mmio_read(struct peci_device *device, u8 bar, u8 seg, + u8 bus, u8 dev, u8 func, u64 address, u32 *data); + +#endif /* __LINUX_PECI_CPU_H */ diff --git a/include/linux/peci.h b/include/linux/peci.h index dcf1c53f4e40..ce43705eaac4 100644 --- a/include/linux/peci.h +++ b/include/linux/peci.h @@ -14,14 +14,6 @@ */ #define PECI_REQUEST_MAX_BUF_SIZE 32 -#define PECI_PCS_PKG_ID 0 /* Package Identifier Read */ -#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ -#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ -#define PECI_PKG_ID_DEVICE_ID 0x0002 /* Uncore Device ID */ -#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ -#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ -#define PECI_PKG_ID_MCA_ERROR_LOG 0x0005 /* Machine Check Status */ - struct peci_controller; struct peci_request; -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:31 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:31 +0200 Subject: [PATCH v2 12/15] hwmon: peci: Add cputemp driver In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-13-iwona.winiarska@intel.com> Add peci-cputemp driver for Digital Thermal Sensor (DTS) thermal readings of the processor package and processor cores that are accessible via the PECI interface. The main use case for the driver (and PECI interface) is out-of-band management, where we're able to obtain the DTS readings from an external entity connected with PECI, e.g. BMC on server platforms. Co-developed-by: Jae Hyun Yoo Signed-off-by: Jae Hyun Yoo Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- MAINTAINERS | 7 + drivers/hwmon/Kconfig | 2 + drivers/hwmon/Makefile | 1 + drivers/hwmon/peci/Kconfig | 18 ++ drivers/hwmon/peci/Makefile | 5 + drivers/hwmon/peci/common.h | 58 ++++ drivers/hwmon/peci/cputemp.c | 591 +++++++++++++++++++++++++++++++++++ 7 files changed, 682 insertions(+) create mode 100644 drivers/hwmon/peci/Kconfig create mode 100644 drivers/hwmon/peci/Makefile create mode 100644 drivers/hwmon/peci/common.h create mode 100644 drivers/hwmon/peci/cputemp.c diff --git a/MAINTAINERS b/MAINTAINERS index 3f5d48e1d143..e36b5c0824e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14512,6 +14512,13 @@ L: platform-driver-x86 at vger.kernel.org S: Maintained F: drivers/platform/x86/peaq-wmi.c +PECI HARDWARE MONITORING DRIVERS +M: Iwona Winiarska +R: Jae Hyun Yoo +L: linux-hwmon at vger.kernel.org +S: Supported +F: drivers/hwmon/peci/ + PECI SUBSYSTEM M: Iwona Winiarska R: Jae Hyun Yoo diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index e3675377bc5d..61c0e3404415 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1507,6 +1507,8 @@ config SENSORS_PCF8591 These devices are hard to detect and rarely found on mainstream hardware. If unsure, say N. +source "drivers/hwmon/peci/Kconfig" + source "drivers/hwmon/pmbus/Kconfig" config SENSORS_PWM_FAN diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index d712c61c1f5e..f52331f212ed 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -202,6 +202,7 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o obj-$(CONFIG_SENSORS_OCC) += occ/ +obj-$(CONFIG_SENSORS_PECI) += peci/ obj-$(CONFIG_PMBUS) += pmbus/ ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig new file mode 100644 index 000000000000..e10eed68d70a --- /dev/null +++ b/drivers/hwmon/peci/Kconfig @@ -0,0 +1,18 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config SENSORS_PECI_CPUTEMP + tristate "PECI CPU temperature monitoring client" + depends on PECI + select SENSORS_PECI + select PECI_CPU + help + If you say yes here you get support for the generic Intel PECI + cputemp driver which provides Digital Thermal Sensor (DTS) thermal + readings of the CPU package and CPU cores that are accessible via + the processor PECI interface. + + This driver can also be built as a module. If so, the module + will be called peci-cputemp. + +config SENSORS_PECI + tristate diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile new file mode 100644 index 000000000000..e8a0ada5ab1f --- /dev/null +++ b/drivers/hwmon/peci/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only + +peci-cputemp-y := cputemp.o + +obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h new file mode 100644 index 000000000000..734506b0eca2 --- /dev/null +++ b/drivers/hwmon/peci/common.h @@ -0,0 +1,58 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (c) 2021 Intel Corporation */ + +#include +#include + +#ifndef __PECI_HWMON_COMMON_H +#define __PECI_HWMON_COMMON_H + +#define PECI_HWMON_UPDATE_INTERVAL HZ + +/** + * struct peci_sensor_state - PECI state information + * @valid: flag to indicate the sensor value is valid + * @last_updated: time of the last update in jiffies + * @lock: mutex to protect sensor access + */ +struct peci_sensor_state { + bool valid; + unsigned long last_updated; + struct mutex lock; /* protect sensor access */ +}; + +/** + * struct peci_sensor_data - PECI sensor information + * @value: sensor value in milli units + * @state: sensor update state + */ + +struct peci_sensor_data { + s32 value; + struct peci_sensor_state state; +}; + +/** + * peci_sensor_need_update() - check whether sensor update is needed or not + * @sensor: pointer to sensor data struct + * + * Return: true if update is needed, false if not. + */ + +static inline bool peci_sensor_need_update(struct peci_sensor_state *state) +{ + return !state->valid || + time_after(jiffies, state->last_updated + PECI_HWMON_UPDATE_INTERVAL); +} + +/** + * peci_sensor_mark_updated() - mark the sensor is updated + * @sensor: pointer to sensor data struct + */ +static inline void peci_sensor_mark_updated(struct peci_sensor_state *state) +{ + state->valid = true; + state->last_updated = jiffies; +} + +#endif /* __PECI_HWMON_COMMON_H */ diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c new file mode 100644 index 000000000000..9c6858a9fb6d --- /dev/null +++ b/drivers/hwmon/peci/cputemp.c @@ -0,0 +1,591 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (c) 2018-2021 Intel Corporation + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" + +#define CORE_NUMS_MAX 64 + +#define BASE_CHANNEL_NUMS 5 +#define CPUTEMP_CHANNEL_NUMS (BASE_CHANNEL_NUMS + CORE_NUMS_MAX) + +#define TEMP_TARGET_FAN_TEMP_MASK GENMASK(15, 8) +#define TEMP_TARGET_REF_TEMP_MASK GENMASK(23, 16) +#define TEMP_TARGET_TJ_OFFSET_MASK GENMASK(29, 24) + +#define DTS_MARGIN_MASK GENMASK(15, 0) +#define PCS_MODULE_TEMP_MASK GENMASK(15, 0) + +#define DTS_FIXED_POINT_FRACTION 64 + +struct resolved_cores_reg { + u8 bus; + u8 dev; + u8 func; + u8 offset; +}; + +struct cpu_info { + struct resolved_cores_reg *reg; + u8 min_peci_revision; +}; + +struct peci_temp_target { + s32 tcontrol; + s32 tthrottle; + s32 tjmax; + struct peci_sensor_state state; +}; + +enum peci_temp_target_type { + tcontrol_type, + tthrottle_type, + tjmax_type, + crit_hyst_type, +}; + +struct peci_cputemp { + struct peci_device *peci_dev; + struct device *dev; + const char *name; + const struct cpu_info *gen_info; + struct { + struct peci_temp_target target; + struct peci_sensor_data die; + struct peci_sensor_data dts; + struct peci_sensor_data core[CORE_NUMS_MAX]; + } temp; + const char **coretemp_label; + DECLARE_BITMAP(core_mask, CORE_NUMS_MAX); +}; + +enum cputemp_channels { + channel_die, + channel_dts, + channel_tcontrol, + channel_tthrottle, + channel_tjmax, + channel_core, +}; + +static const char * const cputemp_label[BASE_CHANNEL_NUMS] = { + "Die", + "DTS", + "Tcontrol", + "Tthrottle", + "Tjmax", +}; + +static int update_temp_target(struct peci_cputemp *priv) +{ + s32 tthrottle_offset, tcontrol_margin; + u32 pcs; + int ret; + + if (!peci_sensor_need_update(&priv->temp.target.state)) + return 0; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_TEMP_TARGET, 0, &pcs); + if (ret) + return ret; + + priv->temp.target.tjmax = + FIELD_GET(TEMP_TARGET_REF_TEMP_MASK, pcs) * MILLIDEGREE_PER_DEGREE; + + tcontrol_margin = FIELD_GET(TEMP_TARGET_FAN_TEMP_MASK, pcs); + tcontrol_margin = sign_extend32(tcontrol_margin, 7) * MILLIDEGREE_PER_DEGREE; + priv->temp.target.tcontrol = priv->temp.target.tjmax - tcontrol_margin; + + tthrottle_offset = FIELD_GET(TEMP_TARGET_TJ_OFFSET_MASK, pcs) * MILLIDEGREE_PER_DEGREE; + priv->temp.target.tthrottle = priv->temp.target.tjmax - tthrottle_offset; + + peci_sensor_mark_updated(&priv->temp.target.state); + + return 0; +} + +static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type type, long *val) +{ + int ret; + + mutex_lock(&priv->temp.target.state.lock); + + ret = update_temp_target(priv); + if (ret) + goto unlock; + + switch (type) { + case tcontrol_type: + *val = priv->temp.target.tcontrol; + break; + case tthrottle_type: + *val = priv->temp.target.tthrottle; + break; + case tjmax_type: + *val = priv->temp.target.tjmax; + break; + case crit_hyst_type: + *val = priv->temp.target.tjmax - priv->temp.target.tcontrol; + break; + default: + ret = -EOPNOTSUPP; + break; + } +unlock: + mutex_unlock(&priv->temp.target.state.lock); + + return ret; +} + +/* + * Processors return a value of DTS reading in S10.6 fixed point format + * (16 bits: 10-bit signed magnitude, 6-bit fraction). + * Error codes: + * 0x8000: General sensor error + * 0x8001: Reserved + * 0x8002: Underflow on reading value + * 0x8003-0x81ff: Reserved + */ +static bool dts_valid(s32 val) +{ + return val < 0x8000 || val > 0x81ff; +} + +static s32 dts_to_millidegree(s32 val) +{ + return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / DTS_FIXED_POINT_FRACTION; +} + +static int get_die_temp(struct peci_cputemp *priv, long *val) +{ + long tjmax; + s16 temp; + int ret; + + mutex_lock(&priv->temp.die.state.lock); + if (!peci_sensor_need_update(&priv->temp.die.state)) + goto skip_update; + + ret = peci_temp_read(priv->peci_dev, &temp); + if (ret) + goto err_unlock; + + if (!dts_valid(temp)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tjmax_type, &tjmax); + if (ret) + goto err_unlock; + + priv->temp.die.value = (s32)tjmax + dts_to_millidegree(temp); + + peci_sensor_mark_updated(&priv->temp.die.state); + +skip_update: + *val = priv->temp.die.value; + mutex_unlock(&priv->temp.die.state.lock); + + return 0; + +err_unlock: + mutex_unlock(&priv->temp.die.state.lock); + return ret; +} + +static int get_dts(struct peci_cputemp *priv, long *val) +{ + s32 dts_margin; + long tcontrol; + u32 pcs; + int ret; + + mutex_lock(&priv->temp.dts.state.lock); + if (!peci_sensor_need_update(&priv->temp.dts.state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs); + if (ret) + goto err_unlock; + + dts_margin = FIELD_GET(DTS_MARGIN_MASK, pcs); + if (!dts_valid(dts_margin)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tcontrol_type, &tcontrol); + if (ret) + goto err_unlock; + + /* Note that the tcontrol should be available before calling it */ + priv->temp.dts.value = (s32)tcontrol - dts_to_millidegree(dts_margin); + + peci_sensor_mark_updated(&priv->temp.dts.state); + +skip_update: + *val = priv->temp.dts.value; + mutex_unlock(&priv->temp.dts.state.lock); + + return 0; + +err_unlock: + mutex_unlock(&priv->temp.dts.state.lock); + return ret; +} + +static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val) +{ + s32 core_dts_margin; + long tjmax; + u32 pcs; + int ret; + + mutex_lock(&priv->temp.core[core_index].state.lock); + if (!peci_sensor_need_update(&priv->temp.core[core_index].state)) + goto skip_update; + + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs); + if (ret) + goto err_unlock; + + core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs); + if (!dts_valid(core_dts_margin)) { + ret = -EIO; + goto err_unlock; + } + + ret = get_temp_target(priv, tjmax_type, &tjmax); + if (ret) + goto err_unlock; + + /* Note that the tjmax should be available before calling it */ + priv->temp.core[core_index].value = (s32)tjmax + dts_to_millidegree(core_dts_margin); + + peci_sensor_mark_updated(&priv->temp.core[core_index].state); + +skip_update: + *val = priv->temp.core[core_index].value; + mutex_unlock(&priv->temp.core[core_index].state.lock); + + return 0; + +err_unlock: + mutex_unlock(&priv->temp.core[core_index].state.lock); + return ret; +} + +static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, const char **str) +{ + struct peci_cputemp *priv = dev_get_drvdata(dev); + + if (attr != hwmon_temp_label) + return -EOPNOTSUPP; + + *str = channel < channel_core ? + cputemp_label[channel] : priv->coretemp_label[channel - channel_core]; + + return 0; +} + +static int cputemp_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + struct peci_cputemp *priv = dev_get_drvdata(dev); + + switch (attr) { + case hwmon_temp_input: + switch (channel) { + case channel_die: + return get_die_temp(priv, val); + case channel_dts: + return get_dts(priv, val); + case channel_tcontrol: + return get_temp_target(priv, tcontrol_type, val); + case channel_tthrottle: + return get_temp_target(priv, tthrottle_type, val); + case channel_tjmax: + return get_temp_target(priv, tjmax_type, val); + default: + return get_core_temp(priv, channel - channel_core, val); + } + break; + case hwmon_temp_max: + return get_temp_target(priv, tcontrol_type, val); + case hwmon_temp_crit: + return get_temp_target(priv, tjmax_type, val); + case hwmon_temp_crit_hyst: + return get_temp_target(priv, crit_hyst_type, val); + default: + return -EOPNOTSUPP; + } + + return 0; +} + +static umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + const struct peci_cputemp *priv = data; + + if (channel > CPUTEMP_CHANNEL_NUMS) + return 0; + + if (channel < channel_core) + return 0444; + + if (test_bit(channel - channel_core, priv->core_mask)) + return 0444; + + return 0; +} + +static int init_core_mask(struct peci_cputemp *priv) +{ + struct peci_device *peci_dev = priv->peci_dev; + struct resolved_cores_reg *reg = priv->gen_info->reg; + u64 core_mask; + u32 data; + int ret; + + /* Get the RESOLVED_CORES register value */ + switch (peci_dev->info.model) { + case INTEL_FAM6_ICELAKE_X: + case INTEL_FAM6_ICELAKE_D: + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, + reg->func, reg->offset + 4, &data); + if (ret) + return ret; + + core_mask = (u64)data << 32; + + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, + reg->func, reg->offset, &data); + if (ret) + return ret; + + core_mask |= data; + + break; + default: + ret = peci_pci_local_read(peci_dev, reg->bus, reg->dev, + reg->func, reg->offset, &data); + if (ret) + return ret; + + core_mask = data; + + break; + } + + if (!core_mask) + return -EIO; + + bitmap_from_u64(priv->core_mask, core_mask); + + return 0; +} + +static int create_temp_label(struct peci_cputemp *priv) +{ + unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX); + int i; + + priv->coretemp_label = devm_kzalloc(priv->dev, core_max * sizeof(char *), GFP_KERNEL); + if (!priv->coretemp_label) + return -ENOMEM; + + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) { + priv->coretemp_label[i] = devm_kasprintf(priv->dev, GFP_KERNEL, "Core %d", i); + if (!priv->coretemp_label[i]) + return -ENOMEM; + } + + return 0; +} + +static void check_resolved_cores(struct peci_cputemp *priv) +{ + /* + * Failure to resolve cores is non-critical, we're still able to + * provide other sensor data. + */ + + if (init_core_mask(priv)) + return; + + if (create_temp_label(priv)) + bitmap_zero(priv->core_mask, CORE_NUMS_MAX); +} + +static void sensor_init(struct peci_cputemp *priv) +{ + int i; + + mutex_init(&priv->temp.target.state.lock); + mutex_init(&priv->temp.die.state.lock); + mutex_init(&priv->temp.dts.state.lock); + + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) + mutex_init(&priv->temp.core[i].state.lock); +} + +static const struct hwmon_ops peci_cputemp_ops = { + .is_visible = cputemp_is_visible, + .read_string = cputemp_read_string, + .read = cputemp_read, +}; + +static const u32 peci_cputemp_temp_channel_config[] = { + /* Die temperature */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST, + /* DTS margin */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST, + /* Tcontrol temperature */ + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT, + /* Tthrottle temperature */ + HWMON_T_LABEL | HWMON_T_INPUT, + /* Tjmax temperature */ + HWMON_T_LABEL | HWMON_T_INPUT, + /* Core temperature - for all core channels */ + [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = HWMON_T_LABEL | HWMON_T_INPUT, + 0 +}; + +static const struct hwmon_channel_info peci_cputemp_temp_channel = { + .type = hwmon_temp, + .config = peci_cputemp_temp_channel_config, +}; + +static const struct hwmon_channel_info *peci_cputemp_info[] = { + &peci_cputemp_temp_channel, + NULL +}; + +static const struct hwmon_chip_info peci_cputemp_chip_info = { + .ops = &peci_cputemp_ops, + .info = peci_cputemp_info, +}; + +static int peci_cputemp_probe(struct auxiliary_device *adev, + const struct auxiliary_device_id *id) +{ + struct device *dev = &adev->dev; + struct peci_device *peci_dev = to_peci_device(dev->parent); + struct peci_cputemp *priv; + struct device *hwmon_dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_cputemp.cpu%d", + peci_dev->info.socket_id); + if (!priv->name) + return -ENOMEM; + + priv->dev = dev; + priv->peci_dev = peci_dev; + priv->gen_info = (const struct cpu_info *)id->driver_data; + + /* + * This is just a sanity check. Since we're using commands that are + * guaranteed to be supported on a given platform, we should never see + * revision lower than expected. + */ + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) + dev_warn(priv->dev, + "Unexpected PECI revision %#x, some features may be unavailable\n", + peci_dev->info.peci_revision); + + check_resolved_cores(priv); + + sensor_init(priv); + + hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, + priv, &peci_cputemp_chip_info, NULL); + + return PTR_ERR_OR_ZERO(hwmon_dev); +} + +/* + * RESOLVED_CORES PCI configuration register may have different location on + * different platforms. + */ +static struct resolved_cores_reg resolved_cores_reg_hsx = { + .bus = 1, + .dev = 30, + .func = 3, + .offset = 0xb4, +}; + +static struct resolved_cores_reg resolved_cores_reg_icx = { + .bus = 14, + .dev = 30, + .func = 3, + .offset = 0xd0, +}; + +static const struct cpu_info cpu_hsx = { + .reg = &resolved_cores_reg_hsx, + .min_peci_revision = 0x33, +}; + +static const struct cpu_info cpu_icx = { + .reg = &resolved_cores_reg_icx, + .min_peci_revision = 0x40, +}; + +static const struct auxiliary_device_id peci_cputemp_ids[] = { + { + .name = "peci_cpu.cputemp.hsx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.bdx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.bdxd", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.skx", + .driver_data = (kernel_ulong_t)&cpu_hsx, + }, + { + .name = "peci_cpu.cputemp.icx", + .driver_data = (kernel_ulong_t)&cpu_icx, + }, + { + .name = "peci_cpu.cputemp.icxd", + .driver_data = (kernel_ulong_t)&cpu_icx, + }, + { } +}; +MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids); + +static struct auxiliary_driver peci_cputemp_driver = { + .probe = peci_cputemp_probe, + .id_table = peci_cputemp_ids, +}; + +module_auxiliary_driver(peci_cputemp_driver); + +MODULE_AUTHOR("Jae Hyun Yoo "); +MODULE_AUTHOR("Iwona Winiarska "); +MODULE_DESCRIPTION("PECI cputemp driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(PECI_CPU); -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:33 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:33 +0200 Subject: [PATCH v2 14/15] docs: hwmon: Document PECI drivers In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-15-iwona.winiarska@intel.com> From: Jae Hyun Yoo Add documentation for peci-cputemp driver that provides DTS thermal readings for CPU packages and CPU cores, and peci-dimmtemp driver that provides Temperature Sensor on DIMM readings. Signed-off-by: Jae Hyun Yoo Co-developed-by: Iwona Winiarska Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- Documentation/hwmon/index.rst | 2 + Documentation/hwmon/peci-cputemp.rst | 90 +++++++++++++++++++++++++++ Documentation/hwmon/peci-dimmtemp.rst | 57 +++++++++++++++++ MAINTAINERS | 2 + 4 files changed, 151 insertions(+) create mode 100644 Documentation/hwmon/peci-cputemp.rst create mode 100644 Documentation/hwmon/peci-dimmtemp.rst diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst index bc01601ea81a..cc76b5b3f791 100644 --- a/Documentation/hwmon/index.rst +++ b/Documentation/hwmon/index.rst @@ -154,6 +154,8 @@ Hardware Monitoring Kernel Drivers pcf8591 pim4328 pm6764tr + peci-cputemp + peci-dimmtemp pmbus powr1220 pxe1610 diff --git a/Documentation/hwmon/peci-cputemp.rst b/Documentation/hwmon/peci-cputemp.rst new file mode 100644 index 000000000000..fe0422248dc5 --- /dev/null +++ b/Documentation/hwmon/peci-cputemp.rst @@ -0,0 +1,90 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +Kernel driver peci-cputemp +========================== + +Supported chips: + One of Intel server CPUs listed below which is connected to a PECI bus. + * Intel Xeon E5/E7 v3 server processors + Intel Xeon E5-14xx v3 family + Intel Xeon E5-24xx v3 family + Intel Xeon E5-16xx v3 family + Intel Xeon E5-26xx v3 family + Intel Xeon E5-46xx v3 family + Intel Xeon E7-48xx v3 family + Intel Xeon E7-88xx v3 family + * Intel Xeon E5/E7 v4 server processors + Intel Xeon E5-16xx v4 family + Intel Xeon E5-26xx v4 family + Intel Xeon E5-46xx v4 family + Intel Xeon E7-48xx v4 family + Intel Xeon E7-88xx v4 family + * Intel Xeon Scalable server processors + Intel Xeon D family + Intel Xeon Bronze family + Intel Xeon Silver family + Intel Xeon Gold family + Intel Xeon Platinum family + + Datasheet: Available from http://www.intel.com/design/literature.htm + +Author: Jae Hyun Yoo + +Description +----------- + +This driver implements a generic PECI hwmon feature which provides Digital +Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that are +accessible via the processor PECI interface. + +All temperature values are given in millidegree Celsius and will be measurable +only when the target CPU is powered on. + +Sysfs interface +------------------- + +======================= ======================================================= +temp1_label "Die" +temp1_input Provides current die temperature of the CPU package. +temp1_max Provides thermal control temperature of the CPU package + which is also known as Tcontrol. +temp1_crit Provides shutdown temperature of the CPU package which + is also known as the maximum processor junction + temperature, Tjmax or Tprochot. +temp1_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of + the CPU package. + +temp2_label "DTS" +temp2_input Provides current temperature of the CPU package scaled + to match DTS thermal profile. +temp2_max Provides thermal control temperature of the CPU package + which is also known as Tcontrol. +temp2_crit Provides shutdown temperature of the CPU package which + is also known as the maximum processor junction + temperature, Tjmax or Tprochot. +temp2_crit_hyst Provides the hysteresis value from Tcontrol to Tjmax of + the CPU package. + +temp3_label "Tcontrol" +temp3_input Provides current Tcontrol temperature of the CPU + package which is also known as Fan Temperature target. + Indicates the relative value from thermal monitor trip + temperature at which fans should be engaged. +temp3_crit Provides Tcontrol critical value of the CPU package + which is same to Tjmax. + +temp4_label "Tthrottle" +temp4_input Provides current Tthrottle temperature of the CPU + package. Used for throttling temperature. If this value + is allowed and lower than Tjmax - the throttle will + occur and reported at lower than Tjmax. + +temp5_label "Tjmax" +temp5_input Provides the maximum junction temperature, Tjmax of the + CPU package. + +temp[6-N]_label Provides string "Core X", where X is resolved core + number. +temp[6-N]_input Provides current temperature of each core. + +======================= ======================================================= diff --git a/Documentation/hwmon/peci-dimmtemp.rst b/Documentation/hwmon/peci-dimmtemp.rst new file mode 100644 index 000000000000..e562aed620de --- /dev/null +++ b/Documentation/hwmon/peci-dimmtemp.rst @@ -0,0 +1,57 @@ +.. SPDX-License-Identifier: GPL-2.0 + +Kernel driver peci-dimmtemp +=========================== + +Supported chips: + One of Intel server CPUs listed below which is connected to a PECI bus. + * Intel Xeon E5/E7 v3 server processors + Intel Xeon E5-14xx v3 family + Intel Xeon E5-24xx v3 family + Intel Xeon E5-16xx v3 family + Intel Xeon E5-26xx v3 family + Intel Xeon E5-46xx v3 family + Intel Xeon E7-48xx v3 family + Intel Xeon E7-88xx v3 family + * Intel Xeon E5/E7 v4 server processors + Intel Xeon E5-16xx v4 family + Intel Xeon E5-26xx v4 family + Intel Xeon E5-46xx v4 family + Intel Xeon E7-48xx v4 family + Intel Xeon E7-88xx v4 family + * Intel Xeon Scalable server processors + Intel Xeon D family + Intel Xeon Bronze family + Intel Xeon Silver family + Intel Xeon Gold family + Intel Xeon Platinum family + + Datasheet: Available from http://www.intel.com/design/literature.htm + +Author: Jae Hyun Yoo + +Description +----------- + +This driver implements a generic PECI hwmon feature which provides +Temperature sensor on DIMM readings that are accessible via the processor PECI interface. + +All temperature values are given in millidegree Celsius and will be measurable +only when the target CPU is powered on. + +Sysfs interface +------------------- + +======================= ======================================================= + +temp[N]_label Provides string "DIMM CI", where C is DIMM channel and + I is DIMM index of the populated DIMM. +temp[N]_input Provides current temperature of the populated DIMM. +temp[N]_max Provides thermal control temperature of the DIMM. +temp[N]_crit Provides shutdown temperature of the DIMM. + +======================= ======================================================= + +Note: + DIMM temperature attributes will appear when the client CPU's BIOS + completes memory training and testing. diff --git a/MAINTAINERS b/MAINTAINERS index e36b5c0824e3..4861a214d9fe 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14517,6 +14517,8 @@ M: Iwona Winiarska R: Jae Hyun Yoo L: linux-hwmon at vger.kernel.org S: Supported +F: Documentation/hwmon/peci-cputemp.rst +F: Documentation/hwmon/peci-dimmtemp.rst F: drivers/hwmon/peci/ PECI SUBSYSTEM -- 2.31.1 From iwona.winiarska at intel.com Tue Aug 3 21:31:34 2021 From: iwona.winiarska at intel.com (Iwona Winiarska) Date: Tue, 3 Aug 2021 13:31:34 +0200 Subject: [PATCH v2 15/15] docs: Add PECI documentation In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: <20210803113134.2262882-16-iwona.winiarska@intel.com> Add a brief overview of PECI and PECI wire interface. The documentation also contains kernel-doc for PECI subsystem internals and PECI CPU Driver API. Signed-off-by: Iwona Winiarska Reviewed-by: Pierre-Louis Bossart --- Documentation/index.rst | 1 + Documentation/peci/index.rst | 16 ++++++++++++ Documentation/peci/peci.rst | 48 ++++++++++++++++++++++++++++++++++++ MAINTAINERS | 1 + 4 files changed, 66 insertions(+) create mode 100644 Documentation/peci/index.rst create mode 100644 Documentation/peci/peci.rst diff --git a/Documentation/index.rst b/Documentation/index.rst index 54ce34fd6fbd..7671f2cd474f 100644 --- a/Documentation/index.rst +++ b/Documentation/index.rst @@ -137,6 +137,7 @@ needed). misc-devices/index scheduler/index mhi/index + peci/index Architecture-agnostic documentation ----------------------------------- diff --git a/Documentation/peci/index.rst b/Documentation/peci/index.rst new file mode 100644 index 000000000000..989de10416e7 --- /dev/null +++ b/Documentation/peci/index.rst @@ -0,0 +1,16 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +==================== +Linux PECI Subsystem +==================== + +.. toctree:: + + peci + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` diff --git a/Documentation/peci/peci.rst b/Documentation/peci/peci.rst new file mode 100644 index 000000000000..a12c8e10c4a9 --- /dev/null +++ b/Documentation/peci/peci.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +======== +Overview +======== + +The Platform Environment Control Interface (PECI) is a communication +interface between Intel processor and management controllers +(e.g. Baseboard Management Controller, BMC). +PECI provides services that allow the management controller to +configure, monitor and debug platform by accessing various registers. +It defines a dedicated command protocol, where the management +controller is acting as a PECI originator and the processor - as +a PECI responder. +PECI can be used in both single processor and multiple-processor based +systems. + +NOTE: +Intel PECI specification is not released as a dedicated document, +instead it is a part of External Design Specification (EDS) for given +Intel CPU. External Design Specifications are usually not publicly +available. + +PECI Wire +--------- + +PECI Wire interface uses a single wire for self-clocking and data +transfer. It does not require any additional control lines - the +physical layer is a self-clocked one-wire bus signal that begins each +bit with a driven, rising edge from an idle near zero volts. The +duration of the signal driven high allows to determine whether the bit +value is logic '0' or logic '1'. PECI Wire also includes variable data +rate established with every message. + +For PECI Wire, each processor package will utilize unique, fixed +addresses within a defined range and that address should +have a fixed relationship with the processor socket ID - if one of the +processors is removed, it does not affect addresses of remaining +processors. + +PECI subsystem internals +------------------------ + +.. kernel-doc:: include/linux/peci.h + +PECI CPU Driver API +------------------- +.. kernel-doc:: include/linux/peci-cpu.h diff --git a/MAINTAINERS b/MAINTAINERS index 4861a214d9fe..c50d4d0005e3 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -14527,6 +14527,7 @@ R: Jae Hyun Yoo L: openbmc at lists.ozlabs.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/peci/ +F: Documentation/peci/ F: drivers/peci/ F: include/linux/peci-cpu.h F: include/linux/peci.h -- 2.31.1 From linux at roeck-us.net Wed Aug 4 01:24:14 2021 From: linux at roeck-us.net (Guenter Roeck) Date: Tue, 3 Aug 2021 08:24:14 -0700 Subject: [PATCH v2 12/15] hwmon: peci: Add cputemp driver In-Reply-To: <20210803113134.2262882-13-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-13-iwona.winiarska@intel.com> Message-ID: On 8/3/21 4:31 AM, Iwona Winiarska wrote: > Add peci-cputemp driver for Digital Thermal Sensor (DTS) thermal > readings of the processor package and processor cores that are > accessible via the PECI interface. > > The main use case for the driver (and PECI interface) is out-of-band > management, where we're able to obtain the DTS readings from an external > entity connected with PECI, e.g. BMC on server platforms. > > Co-developed-by: Jae Hyun Yoo > Signed-off-by: Jae Hyun Yoo > Signed-off-by: Iwona Winiarska > Reviewed-by: Pierre-Louis Bossart > --- > MAINTAINERS | 7 + > drivers/hwmon/Kconfig | 2 + > drivers/hwmon/Makefile | 1 + > drivers/hwmon/peci/Kconfig | 18 ++ > drivers/hwmon/peci/Makefile | 5 + > drivers/hwmon/peci/common.h | 58 ++++ > drivers/hwmon/peci/cputemp.c | 591 +++++++++++++++++++++++++++++++++++ > 7 files changed, 682 insertions(+) > create mode 100644 drivers/hwmon/peci/Kconfig > create mode 100644 drivers/hwmon/peci/Makefile > create mode 100644 drivers/hwmon/peci/common.h > create mode 100644 drivers/hwmon/peci/cputemp.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 3f5d48e1d143..e36b5c0824e3 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -14512,6 +14512,13 @@ L: platform-driver-x86 at vger.kernel.org > S: Maintained > F: drivers/platform/x86/peaq-wmi.c > > +PECI HARDWARE MONITORING DRIVERS > +M: Iwona Winiarska > +R: Jae Hyun Yoo > +L: linux-hwmon at vger.kernel.org > +S: Supported > +F: drivers/hwmon/peci/ > + > PECI SUBSYSTEM > M: Iwona Winiarska > R: Jae Hyun Yoo > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig > index e3675377bc5d..61c0e3404415 100644 > --- a/drivers/hwmon/Kconfig > +++ b/drivers/hwmon/Kconfig > @@ -1507,6 +1507,8 @@ config SENSORS_PCF8591 > These devices are hard to detect and rarely found on mainstream > hardware. If unsure, say N. > > +source "drivers/hwmon/peci/Kconfig" > + > source "drivers/hwmon/pmbus/Kconfig" > > config SENSORS_PWM_FAN > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile > index d712c61c1f5e..f52331f212ed 100644 > --- a/drivers/hwmon/Makefile > +++ b/drivers/hwmon/Makefile > @@ -202,6 +202,7 @@ obj-$(CONFIG_SENSORS_WM8350) += wm8350-hwmon.o > obj-$(CONFIG_SENSORS_XGENE) += xgene-hwmon.o > > obj-$(CONFIG_SENSORS_OCC) += occ/ > +obj-$(CONFIG_SENSORS_PECI) += peci/ > obj-$(CONFIG_PMBUS) += pmbus/ > > ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG > diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig > new file mode 100644 > index 000000000000..e10eed68d70a > --- /dev/null > +++ b/drivers/hwmon/peci/Kconfig > @@ -0,0 +1,18 @@ > +# SPDX-License-Identifier: GPL-2.0-only > + > +config SENSORS_PECI_CPUTEMP > + tristate "PECI CPU temperature monitoring client" > + depends on PECI > + select SENSORS_PECI > + select PECI_CPU > + help > + If you say yes here you get support for the generic Intel PECI > + cputemp driver which provides Digital Thermal Sensor (DTS) thermal > + readings of the CPU package and CPU cores that are accessible via > + the processor PECI interface. > + > + This driver can also be built as a module. If so, the module > + will be called peci-cputemp. > + > +config SENSORS_PECI > + tristate > diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile > new file mode 100644 > index 000000000000..e8a0ada5ab1f > --- /dev/null > +++ b/drivers/hwmon/peci/Makefile > @@ -0,0 +1,5 @@ > +# SPDX-License-Identifier: GPL-2.0-only > + > +peci-cputemp-y := cputemp.o > + > +obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o > diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h > new file mode 100644 > index 000000000000..734506b0eca2 > --- /dev/null > +++ b/drivers/hwmon/peci/common.h > @@ -0,0 +1,58 @@ > +/* SPDX-License-Identifier: GPL-2.0-only */ > +/* Copyright (c) 2021 Intel Corporation */ > + > +#include > +#include > + > +#ifndef __PECI_HWMON_COMMON_H > +#define __PECI_HWMON_COMMON_H > + > +#define PECI_HWMON_UPDATE_INTERVAL HZ > + > +/** > + * struct peci_sensor_state - PECI state information > + * @valid: flag to indicate the sensor value is valid > + * @last_updated: time of the last update in jiffies > + * @lock: mutex to protect sensor access > + */ > +struct peci_sensor_state { > + bool valid; > + unsigned long last_updated; > + struct mutex lock; /* protect sensor access */ > +}; > + > +/** > + * struct peci_sensor_data - PECI sensor information > + * @value: sensor value in milli units > + * @state: sensor update state > + */ > + > +struct peci_sensor_data { > + s32 value; > + struct peci_sensor_state state; > +}; > + > +/** > + * peci_sensor_need_update() - check whether sensor update is needed or not > + * @sensor: pointer to sensor data struct > + * > + * Return: true if update is needed, false if not. > + */ > + > +static inline bool peci_sensor_need_update(struct peci_sensor_state *state) > +{ > + return !state->valid || > + time_after(jiffies, state->last_updated + PECI_HWMON_UPDATE_INTERVAL); > +} > + > +/** > + * peci_sensor_mark_updated() - mark the sensor is updated > + * @sensor: pointer to sensor data struct > + */ > +static inline void peci_sensor_mark_updated(struct peci_sensor_state *state) > +{ > + state->valid = true; > + state->last_updated = jiffies; > +} > + > +#endif /* __PECI_HWMON_COMMON_H */ > diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c > new file mode 100644 > index 000000000000..9c6858a9fb6d > --- /dev/null > +++ b/drivers/hwmon/peci/cputemp.c > @@ -0,0 +1,591 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (c) 2018-2021 Intel Corporation > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "common.h" > + > +#define CORE_NUMS_MAX 64 > + > +#define BASE_CHANNEL_NUMS 5 > +#define CPUTEMP_CHANNEL_NUMS (BASE_CHANNEL_NUMS + CORE_NUMS_MAX) > + > +#define TEMP_TARGET_FAN_TEMP_MASK GENMASK(15, 8) > +#define TEMP_TARGET_REF_TEMP_MASK GENMASK(23, 16) > +#define TEMP_TARGET_TJ_OFFSET_MASK GENMASK(29, 24) > + > +#define DTS_MARGIN_MASK GENMASK(15, 0) > +#define PCS_MODULE_TEMP_MASK GENMASK(15, 0) > + > +#define DTS_FIXED_POINT_FRACTION 64 > + > +struct resolved_cores_reg { > + u8 bus; > + u8 dev; > + u8 func; > + u8 offset; > +}; > + > +struct cpu_info { > + struct resolved_cores_reg *reg; > + u8 min_peci_revision; > +}; > + > +struct peci_temp_target { > + s32 tcontrol; > + s32 tthrottle; > + s32 tjmax; > + struct peci_sensor_state state; > +}; > + > +enum peci_temp_target_type { > + tcontrol_type, > + tthrottle_type, > + tjmax_type, > + crit_hyst_type, > +}; > + > +struct peci_cputemp { > + struct peci_device *peci_dev; > + struct device *dev; > + const char *name; > + const struct cpu_info *gen_info; > + struct { > + struct peci_temp_target target; > + struct peci_sensor_data die; > + struct peci_sensor_data dts; > + struct peci_sensor_data core[CORE_NUMS_MAX]; > + } temp; > + const char **coretemp_label; > + DECLARE_BITMAP(core_mask, CORE_NUMS_MAX); > +}; > + > +enum cputemp_channels { > + channel_die, > + channel_dts, > + channel_tcontrol, > + channel_tthrottle, > + channel_tjmax, > + channel_core, > +}; > + > +static const char * const cputemp_label[BASE_CHANNEL_NUMS] = { > + "Die", > + "DTS", > + "Tcontrol", > + "Tthrottle", > + "Tjmax", > +}; > + > +static int update_temp_target(struct peci_cputemp *priv) > +{ > + s32 tthrottle_offset, tcontrol_margin; > + u32 pcs; > + int ret; > + > + if (!peci_sensor_need_update(&priv->temp.target.state)) > + return 0; > + > + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_TEMP_TARGET, 0, &pcs); > + if (ret) > + return ret; > + > + priv->temp.target.tjmax = > + FIELD_GET(TEMP_TARGET_REF_TEMP_MASK, pcs) * MILLIDEGREE_PER_DEGREE; > + > + tcontrol_margin = FIELD_GET(TEMP_TARGET_FAN_TEMP_MASK, pcs); > + tcontrol_margin = sign_extend32(tcontrol_margin, 7) * MILLIDEGREE_PER_DEGREE; > + priv->temp.target.tcontrol = priv->temp.target.tjmax - tcontrol_margin; > + > + tthrottle_offset = FIELD_GET(TEMP_TARGET_TJ_OFFSET_MASK, pcs) * MILLIDEGREE_PER_DEGREE; > + priv->temp.target.tthrottle = priv->temp.target.tjmax - tthrottle_offset; > + > + peci_sensor_mark_updated(&priv->temp.target.state); > + > + return 0; > +} > + > +static int get_temp_target(struct peci_cputemp *priv, enum peci_temp_target_type type, long *val) > +{ > + int ret; > + > + mutex_lock(&priv->temp.target.state.lock); > + > + ret = update_temp_target(priv); > + if (ret) > + goto unlock; > + > + switch (type) { > + case tcontrol_type: > + *val = priv->temp.target.tcontrol; > + break; > + case tthrottle_type: > + *val = priv->temp.target.tthrottle; > + break; > + case tjmax_type: > + *val = priv->temp.target.tjmax; > + break; > + case crit_hyst_type: > + *val = priv->temp.target.tjmax - priv->temp.target.tcontrol; > + break; > + default: > + ret = -EOPNOTSUPP; > + break; > + } > +unlock: > + mutex_unlock(&priv->temp.target.state.lock); > + > + return ret; > +} > + > +/* > + * Processors return a value of DTS reading in S10.6 fixed point format > + * (16 bits: 10-bit signed magnitude, 6-bit fraction). > + * Error codes: > + * 0x8000: General sensor error > + * 0x8001: Reserved > + * 0x8002: Underflow on reading value > + * 0x8003-0x81ff: Reserved > + */ > +static bool dts_valid(s32 val) > +{ > + return val < 0x8000 || val > 0x81ff; > +} > + > +static s32 dts_to_millidegree(s32 val) > +{ > + return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / DTS_FIXED_POINT_FRACTION; > +} > + > +static int get_die_temp(struct peci_cputemp *priv, long *val) > +{ > + long tjmax; > + s16 temp; > + int ret; > + > + mutex_lock(&priv->temp.die.state.lock); > + if (!peci_sensor_need_update(&priv->temp.die.state)) > + goto skip_update; > + > + ret = peci_temp_read(priv->peci_dev, &temp); > + if (ret) > + goto err_unlock; > + > + if (!dts_valid(temp)) { > + ret = -EIO; > + goto err_unlock; > + } > + > + ret = get_temp_target(priv, tjmax_type, &tjmax); > + if (ret) > + goto err_unlock; > + > + priv->temp.die.value = (s32)tjmax + dts_to_millidegree(temp); > + > + peci_sensor_mark_updated(&priv->temp.die.state); > + > +skip_update: > + *val = priv->temp.die.value; > + mutex_unlock(&priv->temp.die.state.lock); > + > + return 0; > + > +err_unlock: > + mutex_unlock(&priv->temp.die.state.lock); > + return ret; > +} > + > +static int get_dts(struct peci_cputemp *priv, long *val) > +{ > + s32 dts_margin; > + long tcontrol; > + u32 pcs; > + int ret; > + > + mutex_lock(&priv->temp.dts.state.lock); > + if (!peci_sensor_need_update(&priv->temp.dts.state)) > + goto skip_update; > + > + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, &pcs); > + if (ret) > + goto err_unlock; > + > + dts_margin = FIELD_GET(DTS_MARGIN_MASK, pcs); > + if (!dts_valid(dts_margin)) { > + ret = -EIO; > + goto err_unlock; > + } > + > + ret = get_temp_target(priv, tcontrol_type, &tcontrol); > + if (ret) > + goto err_unlock; > + > + /* Note that the tcontrol should be available before calling it */ > + priv->temp.dts.value = (s32)tcontrol - dts_to_millidegree(dts_margin); > + > + peci_sensor_mark_updated(&priv->temp.dts.state); > + > +skip_update: > + *val = priv->temp.dts.value; > + mutex_unlock(&priv->temp.dts.state.lock); > + > + return 0; > + > +err_unlock: > + mutex_unlock(&priv->temp.dts.state.lock); > + return ret; Simplify (see below) > +} > + > +static int get_core_temp(struct peci_cputemp *priv, int core_index, long *val) > +{ > + s32 core_dts_margin; > + long tjmax; > + u32 pcs; > + int ret; int ret = 0; to handle simplification below. > + > + mutex_lock(&priv->temp.core[core_index].state.lock); > + if (!peci_sensor_need_update(&priv->temp.core[core_index].state)) > + goto skip_update; > + > + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, core_index, &pcs); > + if (ret) > + goto err_unlock; > + > + core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs); > + if (!dts_valid(core_dts_margin)) { > + ret = -EIO; > + goto err_unlock; > + } > + > + ret = get_temp_target(priv, tjmax_type, &tjmax); > + if (ret) > + goto err_unlock; > + > + /* Note that the tjmax should be available before calling it */ > + priv->temp.core[core_index].value = (s32)tjmax + dts_to_millidegree(core_dts_margin); > + > + peci_sensor_mark_updated(&priv->temp.core[core_index].state); > + > +skip_update: > + *val = priv->temp.core[core_index].value; > + mutex_unlock(&priv->temp.core[core_index].state.lock); > + > + return 0; > + > +err_unlock: > + mutex_unlock(&priv->temp.core[core_index].state.lock); > + return ret; Simplify: skip_update: *val = priv->temp.core[core_index].value; err_unlock: mutex_unlock(&priv->temp.core[core_index].state.lock); return ret; > +} > + > +static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types type, > + u32 attr, int channel, const char **str) > +{ > + struct peci_cputemp *priv = dev_get_drvdata(dev); > + > + if (attr != hwmon_temp_label) > + return -EOPNOTSUPP; > + > + *str = channel < channel_core ? > + cputemp_label[channel] : priv->coretemp_label[channel - channel_core]; > + > + return 0; > +} > + > +static int cputemp_read(struct device *dev, enum hwmon_sensor_types type, > + u32 attr, int channel, long *val) > +{ > + struct peci_cputemp *priv = dev_get_drvdata(dev); > + > + switch (attr) { > + case hwmon_temp_input: > + switch (channel) { > + case channel_die: > + return get_die_temp(priv, val); > + case channel_dts: > + return get_dts(priv, val); > + case channel_tcontrol: > + return get_temp_target(priv, tcontrol_type, val); > + case channel_tthrottle: > + return get_temp_target(priv, tthrottle_type, val); > + case channel_tjmax: > + return get_temp_target(priv, tjmax_type, val); > + default: > + return get_core_temp(priv, channel - channel_core, val); > + } > + break; > + case hwmon_temp_max: > + return get_temp_target(priv, tcontrol_type, val); > + case hwmon_temp_crit: > + return get_temp_target(priv, tjmax_type, val); > + case hwmon_temp_crit_hyst: > + return get_temp_target(priv, crit_hyst_type, val); > + default: > + return -EOPNOTSUPP; > + } > + > + return 0; > +} > + > +static umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types type, > + u32 attr, int channel) > +{ > + const struct peci_cputemp *priv = data; > + > + if (channel > CPUTEMP_CHANNEL_NUMS) > + return 0; > + > + if (channel < channel_core) > + return 0444; > + > + if (test_bit(channel - channel_core, priv->core_mask)) > + return 0444; > + > + return 0; > +} > + > +static int init_core_mask(struct peci_cputemp *priv) > +{ > + struct peci_device *peci_dev = priv->peci_dev; > + struct resolved_cores_reg *reg = priv->gen_info->reg; > + u64 core_mask; > + u32 data; > + int ret; > + > + /* Get the RESOLVED_CORES register value */ > + switch (peci_dev->info.model) { > + case INTEL_FAM6_ICELAKE_X: > + case INTEL_FAM6_ICELAKE_D: > + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, > + reg->func, reg->offset + 4, &data); > + if (ret) > + return ret; > + > + core_mask = (u64)data << 32; > + > + ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg->dev, > + reg->func, reg->offset, &data); > + if (ret) > + return ret; > + > + core_mask |= data; > + > + break; > + default: > + ret = peci_pci_local_read(peci_dev, reg->bus, reg->dev, > + reg->func, reg->offset, &data); > + if (ret) > + return ret; > + > + core_mask = data; > + > + break; > + } > + > + if (!core_mask) > + return -EIO; > + > + bitmap_from_u64(priv->core_mask, core_mask); > + > + return 0; > +} > + > +static int create_temp_label(struct peci_cputemp *priv) > +{ > + unsigned long core_max = find_last_bit(priv->core_mask, CORE_NUMS_MAX); > + int i; > + > + priv->coretemp_label = devm_kzalloc(priv->dev, core_max * sizeof(char *), GFP_KERNEL); > + if (!priv->coretemp_label) > + return -ENOMEM; > + > + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) { > + priv->coretemp_label[i] = devm_kasprintf(priv->dev, GFP_KERNEL, "Core %d", i); > + if (!priv->coretemp_label[i]) > + return -ENOMEM; > + } > + > + return 0; > +} > + > +static void check_resolved_cores(struct peci_cputemp *priv) > +{ > + /* > + * Failure to resolve cores is non-critical, we're still able to > + * provide other sensor data. > + */ > + > + if (init_core_mask(priv)) > + return; > + > + if (create_temp_label(priv)) > + bitmap_zero(priv->core_mask, CORE_NUMS_MAX); > +} > + > +static void sensor_init(struct peci_cputemp *priv) > +{ > + int i; > + > + mutex_init(&priv->temp.target.state.lock); > + mutex_init(&priv->temp.die.state.lock); > + mutex_init(&priv->temp.dts.state.lock); > + > + for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) > + mutex_init(&priv->temp.core[i].state.lock); > +} > + > +static const struct hwmon_ops peci_cputemp_ops = { > + .is_visible = cputemp_is_visible, > + .read_string = cputemp_read_string, > + .read = cputemp_read, > +}; > + > +static const u32 peci_cputemp_temp_channel_config[] = { > + /* Die temperature */ > + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST, > + /* DTS margin */ > + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | HWMON_T_CRIT_HYST, > + /* Tcontrol temperature */ > + HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT, > + /* Tthrottle temperature */ > + HWMON_T_LABEL | HWMON_T_INPUT, > + /* Tjmax temperature */ > + HWMON_T_LABEL | HWMON_T_INPUT, > + /* Core temperature - for all core channels */ > + [channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = HWMON_T_LABEL | HWMON_T_INPUT, > + 0 > +}; > + > +static const struct hwmon_channel_info peci_cputemp_temp_channel = { > + .type = hwmon_temp, > + .config = peci_cputemp_temp_channel_config, > +}; > + > +static const struct hwmon_channel_info *peci_cputemp_info[] = { > + &peci_cputemp_temp_channel, > + NULL > +}; > + > +static const struct hwmon_chip_info peci_cputemp_chip_info = { > + .ops = &peci_cputemp_ops, > + .info = peci_cputemp_info, > +}; > + > +static int peci_cputemp_probe(struct auxiliary_device *adev, > + const struct auxiliary_device_id *id) > +{ > + struct device *dev = &adev->dev; > + struct peci_device *peci_dev = to_peci_device(dev->parent); > + struct peci_cputemp *priv; > + struct device *hwmon_dev; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_cputemp.cpu%d", > + peci_dev->info.socket_id); > + if (!priv->name) > + return -ENOMEM; > + > + priv->dev = dev; > + priv->peci_dev = peci_dev; > + priv->gen_info = (const struct cpu_info *)id->driver_data; > + > + /* > + * This is just a sanity check. Since we're using commands that are > + * guaranteed to be supported on a given platform, we should never see > + * revision lower than expected. > + */ > + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) > + dev_warn(priv->dev, > + "Unexpected PECI revision %#x, some features may be unavailable\n", > + peci_dev->info.peci_revision); > + > + check_resolved_cores(priv); > + > + sensor_init(priv); > + > + hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, > + priv, &peci_cputemp_chip_info, NULL); > + > + return PTR_ERR_OR_ZERO(hwmon_dev); > +} > + > +/* > + * RESOLVED_CORES PCI configuration register may have different location on > + * different platforms. > + */ > +static struct resolved_cores_reg resolved_cores_reg_hsx = { > + .bus = 1, > + .dev = 30, > + .func = 3, > + .offset = 0xb4, > +}; > + > +static struct resolved_cores_reg resolved_cores_reg_icx = { > + .bus = 14, > + .dev = 30, > + .func = 3, > + .offset = 0xd0, > +}; > + > +static const struct cpu_info cpu_hsx = { > + .reg = &resolved_cores_reg_hsx, > + .min_peci_revision = 0x33, > +}; > + > +static const struct cpu_info cpu_icx = { > + .reg = &resolved_cores_reg_icx, > + .min_peci_revision = 0x40, > +}; > + > +static const struct auxiliary_device_id peci_cputemp_ids[] = { > + { > + .name = "peci_cpu.cputemp.hsx", > + .driver_data = (kernel_ulong_t)&cpu_hsx, > + }, > + { > + .name = "peci_cpu.cputemp.bdx", > + .driver_data = (kernel_ulong_t)&cpu_hsx, > + }, > + { > + .name = "peci_cpu.cputemp.bdxd", > + .driver_data = (kernel_ulong_t)&cpu_hsx, > + }, > + { > + .name = "peci_cpu.cputemp.skx", > + .driver_data = (kernel_ulong_t)&cpu_hsx, > + }, > + { > + .name = "peci_cpu.cputemp.icx", > + .driver_data = (kernel_ulong_t)&cpu_icx, > + }, > + { > + .name = "peci_cpu.cputemp.icxd", > + .driver_data = (kernel_ulong_t)&cpu_icx, > + }, > + { } > +}; > +MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids); > + > +static struct auxiliary_driver peci_cputemp_driver = { > + .probe = peci_cputemp_probe, > + .id_table = peci_cputemp_ids, > +}; > + > +module_auxiliary_driver(peci_cputemp_driver); > + > +MODULE_AUTHOR("Jae Hyun Yoo "); > +MODULE_AUTHOR("Iwona Winiarska "); > +MODULE_DESCRIPTION("PECI cputemp driver"); > +MODULE_LICENSE("GPL"); > +MODULE_IMPORT_NS(PECI_CPU); > From linux at roeck-us.net Wed Aug 4 01:39:37 2021 From: linux at roeck-us.net (Guenter Roeck) Date: Tue, 3 Aug 2021 08:39:37 -0700 Subject: [PATCH v2 13/15] hwmon: peci: Add dimmtemp driver In-Reply-To: <20210803113134.2262882-14-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-14-iwona.winiarska@intel.com> Message-ID: <20210803153937.GA337938@roeck-us.net> On Tue, Aug 03, 2021 at 01:31:32PM +0200, Iwona Winiarska wrote: > Add peci-dimmtemp driver for Temperature Sensor on DIMM readings that > are accessible via the processor PECI interface. > > The main use case for the driver (and PECI interface) is out-of-band > management, where we're able to obtain thermal readings from an external > entity connected with PECI, e.g. BMC on server platforms. > > Co-developed-by: Jae Hyun Yoo > Signed-off-by: Jae Hyun Yoo > Signed-off-by: Iwona Winiarska > Reviewed-by: Pierre-Louis Bossart > --- > Note that the timeout was completely removed - we're going to probe > for detected DIMMs every 5 seconds until we reach "stable" state of > either getting correct DIMM data or getting all -EINVAL (which > suggest that the CPU doesn't have any DIMMs). > > drivers/hwmon/peci/Kconfig | 13 + > drivers/hwmon/peci/Makefile | 2 + > drivers/hwmon/peci/dimmtemp.c | 614 ++++++++++++++++++++++++++++++++++ > 3 files changed, 629 insertions(+) > create mode 100644 drivers/hwmon/peci/dimmtemp.c > > diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig > index e10eed68d70a..9d32a57badfe 100644 > --- a/drivers/hwmon/peci/Kconfig > +++ b/drivers/hwmon/peci/Kconfig > @@ -14,5 +14,18 @@ config SENSORS_PECI_CPUTEMP > This driver can also be built as a module. If so, the module > will be called peci-cputemp. > > +config SENSORS_PECI_DIMMTEMP > + tristate "PECI DIMM temperature monitoring client" > + depends on PECI > + select SENSORS_PECI > + select PECI_CPU > + help > + If you say yes here you get support for the generic Intel PECI hwmon > + driver which provides Temperature Sensor on DIMM readings that are > + accessible via the processor PECI interface. > + > + This driver can also be built as a module. If so, the module > + will be called peci-dimmtemp. > + > config SENSORS_PECI > tristate > diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile > index e8a0ada5ab1f..191cfa0227f3 100644 > --- a/drivers/hwmon/peci/Makefile > +++ b/drivers/hwmon/peci/Makefile > @@ -1,5 +1,7 @@ > # SPDX-License-Identifier: GPL-2.0-only > > peci-cputemp-y := cputemp.o > +peci-dimmtemp-y := dimmtemp.o > > obj-$(CONFIG_SENSORS_PECI_CPUTEMP) += peci-cputemp.o > +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP) += peci-dimmtemp.o > diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c > new file mode 100644 > index 000000000000..6264c29bb6c0 > --- /dev/null > +++ b/drivers/hwmon/peci/dimmtemp.c > @@ -0,0 +1,614 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (c) 2018-2021 Intel Corporation > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "common.h" > + > +#define DIMM_MASK_CHECK_DELAY_JIFFIES msecs_to_jiffies(5000) > + > +/* Max number of channel ranks and DIMM index per channel */ > +#define CHAN_RANK_MAX_ON_HSX 8 > +#define DIMM_IDX_MAX_ON_HSX 3 > +#define CHAN_RANK_MAX_ON_BDX 4 > +#define DIMM_IDX_MAX_ON_BDX 3 > +#define CHAN_RANK_MAX_ON_BDXD 2 > +#define DIMM_IDX_MAX_ON_BDXD 2 > +#define CHAN_RANK_MAX_ON_SKX 6 > +#define DIMM_IDX_MAX_ON_SKX 2 > +#define CHAN_RANK_MAX_ON_ICX 8 > +#define DIMM_IDX_MAX_ON_ICX 2 > +#define CHAN_RANK_MAX_ON_ICXD 4 > +#define DIMM_IDX_MAX_ON_ICXD 2 > + > +#define CHAN_RANK_MAX CHAN_RANK_MAX_ON_HSX > +#define DIMM_IDX_MAX DIMM_IDX_MAX_ON_HSX > +#define DIMM_NUMS_MAX (CHAN_RANK_MAX * DIMM_IDX_MAX) > + > +#define CPU_SEG_MASK GENMASK(23, 16) > +#define GET_CPU_SEG(x) (((x) & CPU_SEG_MASK) >> 16) > +#define CPU_BUS_MASK GENMASK(7, 0) > +#define GET_CPU_BUS(x) ((x) & CPU_BUS_MASK) > + > +#define DIMM_TEMP_MAX GENMASK(15, 8) > +#define DIMM_TEMP_CRIT GENMASK(23, 16) > +#define GET_TEMP_MAX(x) (((x) & DIMM_TEMP_MAX) >> 8) > +#define GET_TEMP_CRIT(x) (((x) & DIMM_TEMP_CRIT) >> 16) > + > +struct peci_dimmtemp; > + > +struct dimm_info { > + int chan_rank_max; > + int dimm_idx_max; > + u8 min_peci_revision; > + int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order, > + int chan_rank, u32 *data); > +}; > + > +struct peci_dimm_thresholds { > + long temp_max; > + long temp_crit; > + struct peci_sensor_state state; > +}; > + > +enum peci_dimm_threshold_type { > + temp_max_type, > + temp_crit_type, > +}; > + > +struct peci_dimmtemp { > + struct peci_device *peci_dev; > + struct device *dev; > + const char *name; > + const struct dimm_info *gen_info; > + struct delayed_work detect_work; > + struct { > + struct peci_sensor_data temp; > + struct peci_dimm_thresholds thresholds; > + } dimm[DIMM_NUMS_MAX]; > + char **dimmtemp_label; > + DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX); > +}; > + > +static u8 __dimm_temp(u32 reg, int dimm_order) > +{ > + return (reg >> (dimm_order * 8)) & 0xff; > +} > + > +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long *val) > +{ > + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; > + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; > + u32 data; > + int ret; int ret = 0; > + > + mutex_lock(&priv->dimm[dimm_no].temp.state.lock); > + if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) > + goto skip_update; > + > + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &data); > + if (ret) { > + mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); > + return ret; > + } if (ret) goto unlock; > + > + priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * MILLIDEGREE_PER_DEGREE; > + > + peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state); > + > +skip_update: > + *val = priv->dimm[dimm_no].temp.value; unlock: > + mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); > + return 0; return ret; > +} > + > +static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) > +{ > + int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; > + int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; > + u32 data; > + int ret; > + > + if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state)) > + return 0; > + > + ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, &data); > + if (ret == -ENODATA) /* Use default or previous value */ > + return 0; > + if (ret) > + return ret; > + > + priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * MILLIDEGREE_PER_DEGREE; > + priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * MILLIDEGREE_PER_DEGREE; > + > + peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state); > + > + return 0; > +} > + > +static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum peci_dimm_threshold_type type, > + int dimm_no, long *val) > +{ > + int ret; > + > + mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); > + ret = update_thresholds(priv, dimm_no); > + if (ret) > + goto unlock; > + > + switch (type) { > + case temp_max_type: > + *val = priv->dimm[dimm_no].thresholds.temp_max; > + break; > + case temp_crit_type: > + *val = priv->dimm[dimm_no].thresholds.temp_crit; > + break; > + default: > + ret = -EOPNOTSUPP; > + break; > + } > +unlock: > + mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); > + > + return ret; > +} > + > +static int dimmtemp_read_string(struct device *dev, > + enum hwmon_sensor_types type, > + u32 attr, int channel, const char **str) > +{ > + struct peci_dimmtemp *priv = dev_get_drvdata(dev); > + > + if (attr != hwmon_temp_label) > + return -EOPNOTSUPP; > + > + *str = (const char *)priv->dimmtemp_label[channel]; > + > + return 0; > +} > + > +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, > + u32 attr, int channel, long *val) > +{ > + struct peci_dimmtemp *priv = dev_get_drvdata(dev); > + > + switch (attr) { > + case hwmon_temp_input: > + return get_dimm_temp(priv, channel, val); > + case hwmon_temp_max: > + return get_dimm_thresholds(priv, temp_max_type, channel, val); > + case hwmon_temp_crit: > + return get_dimm_thresholds(priv, temp_crit_type, channel, val); > + default: > + break; > + } > + > + return -EOPNOTSUPP; > +} > + > +static umode_t dimmtemp_is_visible(const void *data, enum hwmon_sensor_types type, > + u32 attr, int channel) > +{ > + const struct peci_dimmtemp *priv = data; > + > + if (test_bit(channel, priv->dimm_mask)) > + return 0444; > + > + return 0; > +} > + > +static const struct hwmon_ops peci_dimmtemp_ops = { > + .is_visible = dimmtemp_is_visible, > + .read_string = dimmtemp_read_string, > + .read = dimmtemp_read, > +}; > + > +static int check_populated_dimms(struct peci_dimmtemp *priv) > +{ > + int chan_rank_max = priv->gen_info->chan_rank_max; > + int dimm_idx_max = priv->gen_info->dimm_idx_max; > + u32 chan_rank_empty = 0; > + u64 dimm_mask = 0; > + int chan_rank, dimm_idx, ret; > + u32 pcs; > + > + BUILD_BUG_ON(CHAN_RANK_MAX > 32); > + BUILD_BUG_ON(DIMM_NUMS_MAX > 64); I don't immediately see the value of those build bugs. What happens if CHAN_RANK_MAX > 32 or DIMM_NUMS_MAX > 64 ? Where do those limits come from ? > + if (chan_rank_max * dimm_idx_max > DIMM_NUMS_MAX) { > + WARN_ONCE(1, "Unsupported number of DIMMs"); Maybe display the values (chan_rank_max and dimm_idx_max). > + return -EINVAL; > + } > + > + for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) { > + ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, chan_rank, &pcs); > + if (ret) { > + /* > + * Overall, we expect either success or -EINVAL in > + * order to determine whether DIMM is populated or not. > + * For anything else - we fall back to defering the Why " - " ? > + * detection to be performed at a later point in time. > + */ > + if (ret == -EINVAL) { > + chan_rank_empty |= BIT(chan_rank); > + continue; > + } > + > + return -EAGAIN; > + } > + > + for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++) > + if (__dimm_temp(pcs, dimm_idx)) > + dimm_mask |= BIT(chan_rank * dimm_idx_max + dimm_idx); > + } > + > + /* If we got all -EINVALs, it means that the CPU doesn't have any DIMMs. */ > + if (chan_rank_empty == GENMASK(chan_rank_max - 1, 0)) > + return -ENODEV; > + > + /* > + * It's possible that memory training is not done yet. In this case we > + * defer the detection to be performed at a later point in time. > + */ > + if (!dimm_mask) > + return -EAGAIN; > + > + dev_dbg(priv->dev, "Scanned populated DIMMs: %#llx\n", dimm_mask); > + > + bitmap_from_u64(priv->dimm_mask, dimm_mask); > + > + return 0; > +} > + > +static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan) > +{ > + int rank = chan / priv->gen_info->dimm_idx_max; > + int idx = chan % priv->gen_info->dimm_idx_max; > + > + priv->dimmtemp_label[chan] = devm_kasprintf(priv->dev, GFP_KERNEL, > + "DIMM %c%d", 'A' + rank, > + idx + 1); > + if (!priv->dimmtemp_label[chan]) > + return -ENOMEM; > + > + return 0; > +} > + > +static const u32 peci_dimmtemp_temp_channel_config[] = { > + [0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT, > + 0 > +}; > + > +static const struct hwmon_channel_info peci_dimmtemp_temp_channel = { > + .type = hwmon_temp, > + .config = peci_dimmtemp_temp_channel_config, > +}; > + > +static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = { > + &peci_dimmtemp_temp_channel, > + NULL > +}; > + > +static const struct hwmon_chip_info peci_dimmtemp_chip_info = { > + .ops = &peci_dimmtemp_ops, > + .info = peci_dimmtemp_temp_info, > +}; > + > +static int create_dimm_temp_info(struct peci_dimmtemp *priv) > +{ > + int ret, i, channels; > + struct device *dev; > + > + /* > + * We expect to either find populated DIMMs and carry on with creating > + * sensors, or find out that there are no DIMMs populated. > + * All other states mean that the platform never reached the state that > + * allows to check DIMM state - causing us to retry later on. > + */ > + ret = check_populated_dimms(priv); > + if (ret == -ENODEV) { > + dev_dbg(priv->dev, "No DIMMs found\n"); > + return 0; > + } else if (ret) { > + schedule_delayed_work(&priv->detect_work, DIMM_MASK_CHECK_DELAY_JIFFIES); > + dev_dbg(priv->dev, "Deferred populating DIMM temp info\n"); > + return ret; > + } > + > + channels = priv->gen_info->chan_rank_max * priv->gen_info->dimm_idx_max; > + > + priv->dimmtemp_label = devm_kzalloc(priv->dev, channels * sizeof(char *), GFP_KERNEL); > + if (!priv->dimmtemp_label) > + return -ENOMEM; > + > + for_each_set_bit(i, priv->dimm_mask, DIMM_NUMS_MAX) { > + ret = create_dimm_temp_label(priv, i); > + if (ret) > + return ret; > + mutex_init(&priv->dimm[i].thresholds.state.lock); > + mutex_init(&priv->dimm[i].temp.state.lock); > + } > + > + dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, priv, > + &peci_dimmtemp_chip_info, NULL); > + if (IS_ERR(dev)) { > + dev_err(priv->dev, "Failed to register hwmon device\n"); > + return PTR_ERR(dev); > + } > + > + dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name); > + > + return 0; > +} > + > +static void create_dimm_temp_info_delayed(struct work_struct *work) > +{ > + struct peci_dimmtemp *priv = container_of(to_delayed_work(work), > + struct peci_dimmtemp, > + detect_work); > + int ret; > + > + ret = create_dimm_temp_info(priv); > + if (ret && ret != -EAGAIN) > + dev_err(priv->dev, "Failed to populate DIMM temp info\n"); > +} > + > +static void remove_delayed_work(void *_priv) > +{ > + struct peci_dimmtemp *priv = _priv; > + > + cancel_delayed_work_sync(&priv->detect_work); > +} > + > +static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct auxiliary_device_id *id) > +{ > + struct device *dev = &adev->dev; > + struct peci_device *peci_dev = to_peci_device(dev->parent); > + struct peci_dimmtemp *priv; > + int ret; > + > + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_dimmtemp.cpu%d", > + peci_dev->info.socket_id); > + if (!priv->name) > + return -ENOMEM; > + > + priv->dev = dev; > + priv->peci_dev = peci_dev; > + priv->gen_info = (const struct dimm_info *)id->driver_data; > + > + /* > + * This is just a sanity check. Since we're using commands that are > + * guaranteed to be supported on a given platform, we should never see > + * revision lower than expected. > + */ > + if (peci_dev->info.peci_revision < priv->gen_info->min_peci_revision) > + dev_warn(priv->dev, > + "Unexpected PECI revision %#x, some features may be unavailable\n", > + peci_dev->info.peci_revision); > + > + INIT_DELAYED_WORK(&priv->detect_work, create_dimm_temp_info_delayed); > + > + ret = devm_add_action_or_reset(priv->dev, remove_delayed_work, priv); > + if (ret) > + return ret; > + > + ret = create_dimm_temp_info(priv); > + if (ret && ret != -EAGAIN) { > + dev_err(dev, "Failed to populate DIMM temp info\n"); > + return ret; > + } > + > + return 0; > +} > + > +static int > +read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) > +{ > + u8 dev, func; > + u16 reg; > + int ret; > + > + /* > + * Device 20, Function 0: IMC 0 channel 0 -> rank 0 > + * Device 20, Function 1: IMC 0 channel 1 -> rank 1 > + * Device 21, Function 0: IMC 0 channel 2 -> rank 2 > + * Device 21, Function 1: IMC 0 channel 3 -> rank 3 > + * Device 23, Function 0: IMC 1 channel 0 -> rank 4 > + * Device 23, Function 1: IMC 1 channel 1 -> rank 5 > + * Device 24, Function 0: IMC 1 channel 2 -> rank 6 > + * Device 24, Function 1: IMC 1 channel 3 -> rank 7 > + */ > + dev = 20 + chan_rank / 2 + chan_rank / 4; > + func = chan_rank % 2; > + reg = 0x120 + dimm_order * 4; > + > + ret = peci_pci_local_read(priv->peci_dev, 1, dev, func, reg, data); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int > +read_thresholds_bdxd(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) > +{ > + u8 dev, func; > + u16 reg; > + int ret; > + > + /* > + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 > + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 > + * Device 12, Function 2: IMC 1 channel 0 -> rank 2 > + * Device 12, Function 6: IMC 1 channel 1 -> rank 3 > + */ > + dev = 10 + chan_rank / 2 * 2; > + func = (chan_rank % 2) ? 6 : 2; > + reg = 0x120 + dimm_order * 4; > + > + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int > +read_thresholds_skx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) > +{ > + u8 dev, func; > + u16 reg; > + int ret; > + > + /* > + * Device 10, Function 2: IMC 0 channel 0 -> rank 0 > + * Device 10, Function 6: IMC 0 channel 1 -> rank 1 > + * Device 11, Function 2: IMC 0 channel 2 -> rank 2 > + * Device 12, Function 2: IMC 1 channel 0 -> rank 3 > + * Device 12, Function 6: IMC 1 channel 1 -> rank 4 > + * Device 13, Function 2: IMC 1 channel 2 -> rank 5 > + */ > + dev = 10 + chan_rank / 3 * 2 + (chan_rank % 3 == 2 ? 1 : 0); > + func = chan_rank % 3 == 1 ? 6 : 2; > + reg = 0x120 + dimm_order * 4; > + > + ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static int > +read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int chan_rank, u32 *data) > +{ > + u32 reg_val; > + u64 offset; > + int ret; > + u8 dev; > + > + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, ®_val); > + if (ret || !(reg_val & BIT(31))) > + return -ENODATA; /* Use default or previous value */ > + > + ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, ®_val); > + if (ret) > + return -ENODATA; /* Use default or previous value */ > + > + /* > + * Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0 > + * Device 26, Offset 264e0: IMC 0 channel 1 -> rank 1 > + * Device 27, Offset 224e0: IMC 1 channel 0 -> rank 2 > + * Device 27, Offset 264e0: IMC 1 channel 1 -> rank 3 > + * Device 28, Offset 224e0: IMC 2 channel 0 -> rank 4 > + * Device 28, Offset 264e0: IMC 2 channel 1 -> rank 5 > + * Device 29, Offset 224e0: IMC 3 channel 0 -> rank 6 > + * Device 29, Offset 264e0: IMC 3 channel 1 -> rank 7 > + */ > + dev = 26 + chan_rank / 2; > + offset = 0x224e0 + dimm_order * 4 + (chan_rank % 2) * 0x4000; > + > + ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), GET_CPU_BUS(reg_val), > + dev, 0, offset, data); > + if (ret) > + return ret; > + > + return 0; > +} > + > +static const struct dimm_info dimm_hsx = { > + .chan_rank_max = CHAN_RANK_MAX_ON_HSX, > + .dimm_idx_max = DIMM_IDX_MAX_ON_HSX, > + .min_peci_revision = 0x33, > + .read_thresholds = &read_thresholds_hsx, > +}; > + > +static const struct dimm_info dimm_bdx = { > + .chan_rank_max = CHAN_RANK_MAX_ON_BDX, > + .dimm_idx_max = DIMM_IDX_MAX_ON_BDX, > + .min_peci_revision = 0x33, > + .read_thresholds = &read_thresholds_hsx, > +}; > + > +static const struct dimm_info dimm_bdxd = { > + .chan_rank_max = CHAN_RANK_MAX_ON_BDXD, > + .dimm_idx_max = DIMM_IDX_MAX_ON_BDXD, > + .min_peci_revision = 0x33, > + .read_thresholds = &read_thresholds_bdxd, > +}; > + > +static const struct dimm_info dimm_skx = { > + .chan_rank_max = CHAN_RANK_MAX_ON_SKX, > + .dimm_idx_max = DIMM_IDX_MAX_ON_SKX, > + .min_peci_revision = 0x33, > + .read_thresholds = &read_thresholds_skx, > +}; > + > +static const struct dimm_info dimm_icx = { > + .chan_rank_max = CHAN_RANK_MAX_ON_ICX, > + .dimm_idx_max = DIMM_IDX_MAX_ON_ICX, > + .min_peci_revision = 0x40, > + .read_thresholds = &read_thresholds_icx, > +}; > + > +static const struct dimm_info dimm_icxd = { > + .chan_rank_max = CHAN_RANK_MAX_ON_ICXD, > + .dimm_idx_max = DIMM_IDX_MAX_ON_ICXD, > + .min_peci_revision = 0x40, > + .read_thresholds = &read_thresholds_icx, > +}; > + > +static const struct auxiliary_device_id peci_dimmtemp_ids[] = { > + { > + .name = "peci_cpu.dimmtemp.hsx", > + .driver_data = (kernel_ulong_t)&dimm_hsx, > + }, > + { > + .name = "peci_cpu.dimmtemp.bdx", > + .driver_data = (kernel_ulong_t)&dimm_bdx, > + }, > + { > + .name = "peci_cpu.dimmtemp.bdxd", > + .driver_data = (kernel_ulong_t)&dimm_bdxd, > + }, > + { > + .name = "peci_cpu.dimmtemp.skx", > + .driver_data = (kernel_ulong_t)&dimm_skx, > + }, > + { > + .name = "peci_cpu.dimmtemp.icx", > + .driver_data = (kernel_ulong_t)&dimm_icx, > + }, > + { > + .name = "peci_cpu.dimmtemp.icxd", > + .driver_data = (kernel_ulong_t)&dimm_icxd, > + }, > + { } > +}; > +MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids); > + > +static struct auxiliary_driver peci_dimmtemp_driver = { > + .probe = peci_dimmtemp_probe, > + .id_table = peci_dimmtemp_ids, > +}; > + > +module_auxiliary_driver(peci_dimmtemp_driver); > + > +MODULE_AUTHOR("Jae Hyun Yoo "); > +MODULE_AUTHOR("Iwona Winiarska "); > +MODULE_DESCRIPTION("PECI dimmtemp driver"); > +MODULE_LICENSE("GPL"); > +MODULE_IMPORT_NS(PECI_CPU); From andrew at aj.id.au Wed Aug 4 14:55:29 2021 From: andrew at aj.id.au (Andrew Jeffery) Date: Wed, 04 Aug 2021 14:25:29 +0930 Subject: [RFC PATCH 0/6] leds: Fix pca955x GPIO pin mappings In-Reply-To: References: <20210723075858.376378-1-andrew@aj.id.au> <6cc64039-f82a-4c1e-ad2c-16fad7aa3178@www.fastmail.com> <50aaf381-8cda-4656-9222-f23fda75d3bc@www.fastmail.com> Message-ID: <7c20e892-1675-47b6-874f-5e42e7bd73ef@www.fastmail.com> On Tue, 3 Aug 2021, at 20:03, Andy Shevchenko wrote: > On Tue, Aug 3, 2021 at 7:07 AM Andrew Jeffery wrote: > > On Thu, 29 Jul 2021, at 17:10, Andy Shevchenko wrote: > > > On Thu, Jul 29, 2021 at 3:39 AM Andrew Jeffery wrote: > > > > On Wed, 28 Jul 2021, at 18:43, Andy Shevchenko wrote: > > > > > On Wed, Jul 28, 2021 at 8:43 AM Andrew Jeffery wrote: > > > > > > However, userspace would never have > > > > > > got the results it expected with the existing driver implementation, so > > > > > > I guess you could argue that no such (useful) userspace exists. Given > > > > > > that, we could adopt the strategy of always defining a gpiochip > > > > > > covering the whole pin space, and parts of the devicetree binding just > > > > > > become redundant. > > > > > > > > > > I'm lost now. GPIO has its own userspace ABI, how does it work right > > > > > now in application to this chip? > > > > > > > > As above, it "works" if the GPIOs specified in the devicetree are > > > > contiguous from line 0. It's broken if they're not. > > > > > > So, "it never works" means there is no bug. Now, what we need is to > > > keep the same enumeration scheme, but if you wish to be used half/half > > > (or any other ratio), the driver should do like the above mentioned > > > PWM, i.e. register entire space and depending on the requestor either > > > proceed with a line or mark it as BUSY. > > > > > > Ideally, looking into what the chip can do, this should be indeed > > > converted to some like pin control + PWM + LED + GPIO drivers. Then > > > the function in pin mux configuration can show what exactly is enabled > > > on the certain line(s). > > > > So just to clarify, you want both solutions here? > > > > 1. A gpiochip that covers the entire pin space > > 2. A pinmux implementation that manages pin allocation to the different drivers > > > > In that case we can largely leave this series as is? We only need to > > adjust how we configure the gpiochip by dropping the pin-mapping > > implementation? > > Nope. It's far from what I think of. Re-reading again your cover > letter it points out that pin mux per se does not exist in the > hardware. In this case things become a bit too complicated, but we > still may manage to handle them. Before I was thinking about this > hierarchy > > 1. pinmux driver (which is actually the main driver here) > 2. LED driver (using regmap API) > 3. GPIO driver (via gpio-regmap) > 4. PWM driver. Okay - I need to look at gpio-regmap, this wasn't something I was aware of. > > Now what we need here is some kind of "virtual" pinmux. Do I > understand correctly? Possibly. My thoughts went to pinctrl as part of its job is mutual exclusion *and* pin mapping, plus we get the nice debugfs interface with the pin allocation details. The need for pin mapping came from trying to stay true to the intent of the existing devicetree binding. If we throw that out and have the gpiochip cover the pin space for the chip then using pinctrl only gives us mutual exclusion and the debugfs interface. pinctrl seems pretty heavy-weight to use *just* for mutual exclusion - with no requirement for pin mapping I feel whether or not we go this way hinges on the utility of debugfs. As outlined earlier, there's no mux hardware, the only thing that changes is software's intent. > > To be clear: I do not like putting everything into one driver when the > logical parts may be separated. Right, its already a bit unwieldy. Andrew From linux at roeck-us.net Thu Aug 5 03:33:35 2021 From: linux at roeck-us.net (Guenter Roeck) Date: Wed, 4 Aug 2021 10:33:35 -0700 Subject: [PATCH v2 13/15] hwmon: peci: Add dimmtemp driver In-Reply-To: References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-14-iwona.winiarska@intel.com> <20210803153937.GA337938@roeck-us.net> Message-ID: On 8/4/21 3:46 AM, Winiarska, Iwona wrote: > On Tue, 2021-08-03 at 08:39 -0700, Guenter Roeck wrote: >> On Tue, Aug 03, 2021 at 01:31:32PM +0200, Iwona Winiarska wrote: >>> Add peci-dimmtemp driver for Temperature Sensor on DIMM readings that >>> are accessible via the processor PECI interface. >>> >>> The main use case for the driver (and PECI interface) is out-of-band >>> management, where we're able to obtain thermal readings from an external >>> entity connected with PECI, e.g. BMC on server platforms. >>> >>> Co-developed-by: Jae Hyun Yoo >>> Signed-off-by: Jae Hyun Yoo >>> Signed-off-by: Iwona Winiarska >>> Reviewed-by: Pierre-Louis Bossart >>> --- >>> Note that the timeout was completely removed - we're going to probe >>> for detected DIMMs every 5 seconds until we reach "stable" state of >>> either getting correct DIMM data or getting all -EINVAL (which >>> suggest that the CPU doesn't have any DIMMs). >>> >>> ?drivers/hwmon/peci/Kconfig??? |? 13 + >>> ?drivers/hwmon/peci/Makefile?? |?? 2 + >>> ?drivers/hwmon/peci/dimmtemp.c | 614 ++++++++++++++++++++++++++++++++++ >>> ?3 files changed, 629 insertions(+) >>> ?create mode 100644 drivers/hwmon/peci/dimmtemp.c >>> >>> diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig >>> index e10eed68d70a..9d32a57badfe 100644 >>> --- a/drivers/hwmon/peci/Kconfig >>> +++ b/drivers/hwmon/peci/Kconfig >>> @@ -14,5 +14,18 @@ config SENSORS_PECI_CPUTEMP >>> ????????? This driver can also be built as a module. If so, the module >>> ????????? will be called peci-cputemp. >>> >>> +config SENSORS_PECI_DIMMTEMP >>> +???????tristate "PECI DIMM temperature monitoring client" >>> +???????depends on PECI >>> +???????select SENSORS_PECI >>> +???????select PECI_CPU >>> +???????help >>> +???????? If you say yes here you get support for the generic Intel PECI >>> hwmon >>> +???????? driver which provides Temperature Sensor on DIMM readings that are >>> +???????? accessible via the processor PECI interface. >>> + >>> +???????? This driver can also be built as a module. If so, the module >>> +???????? will be called peci-dimmtemp. >>> + >>> ?config SENSORS_PECI >>> ????????tristate >>> diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile >>> index e8a0ada5ab1f..191cfa0227f3 100644 >>> --- a/drivers/hwmon/peci/Makefile >>> +++ b/drivers/hwmon/peci/Makefile >>> @@ -1,5 +1,7 @@ >>> ?# SPDX-License-Identifier: GPL-2.0-only >>> >>> ?peci-cputemp-y := cputemp.o >>> +peci-dimmtemp-y := dimmtemp.o >>> >>> ?obj-$(CONFIG_SENSORS_PECI_CPUTEMP)?????+= peci-cputemp.o >>> +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)????+= peci-dimmtemp.o >>> diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c >>> new file mode 100644 >>> index 000000000000..6264c29bb6c0 >>> --- /dev/null >>> +++ b/drivers/hwmon/peci/dimmtemp.c >>> @@ -0,0 +1,614 @@ >>> +// SPDX-License-Identifier: GPL-2.0-only >>> +// Copyright (c) 2018-2021 Intel Corporation >>> + >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> +#include >>> + >>> +#include "common.h" >>> + >>> +#define DIMM_MASK_CHECK_DELAY_JIFFIES??msecs_to_jiffies(5000) >>> + >>> +/* Max number of channel ranks and DIMM index per channel */ >>> +#define CHAN_RANK_MAX_ON_HSX???8 >>> +#define DIMM_IDX_MAX_ON_HSX????3 >>> +#define CHAN_RANK_MAX_ON_BDX???4 >>> +#define DIMM_IDX_MAX_ON_BDX????3 >>> +#define CHAN_RANK_MAX_ON_BDXD??2 >>> +#define DIMM_IDX_MAX_ON_BDXD???2 >>> +#define CHAN_RANK_MAX_ON_SKX???6 >>> +#define DIMM_IDX_MAX_ON_SKX????2 >>> +#define CHAN_RANK_MAX_ON_ICX???8 >>> +#define DIMM_IDX_MAX_ON_ICX????2 >>> +#define CHAN_RANK_MAX_ON_ICXD??4 >>> +#define DIMM_IDX_MAX_ON_ICXD???2 >>> + >>> +#define CHAN_RANK_MAX??????????CHAN_RANK_MAX_ON_HSX >>> +#define DIMM_IDX_MAX???????????DIMM_IDX_MAX_ON_HSX >>> +#define DIMM_NUMS_MAX??????????(CHAN_RANK_MAX * DIMM_IDX_MAX) >>> + >>> +#define CPU_SEG_MASK???????????GENMASK(23, 16) >>> +#define GET_CPU_SEG(x)?????????(((x) & CPU_SEG_MASK) >> 16) >>> +#define CPU_BUS_MASK???????????GENMASK(7, 0) >>> +#define GET_CPU_BUS(x)?????????((x) & CPU_BUS_MASK) >>> + >>> +#define DIMM_TEMP_MAX??????????GENMASK(15, 8) >>> +#define DIMM_TEMP_CRIT?????????GENMASK(23, 16) >>> +#define GET_TEMP_MAX(x)????????????????(((x) & DIMM_TEMP_MAX) >> 8) >>> +#define GET_TEMP_CRIT(x)???????(((x) & DIMM_TEMP_CRIT) >> 16) >>> + >>> +struct peci_dimmtemp; >>> + >>> +struct dimm_info { >>> +???????int chan_rank_max; >>> +???????int dimm_idx_max; >>> +???????u8 min_peci_revision; >>> +???????int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order, >>> +????????????????????????????? int chan_rank, u32 *data); >>> +}; >>> + >>> +struct peci_dimm_thresholds { >>> +???????long temp_max; >>> +???????long temp_crit; >>> +???????struct peci_sensor_state state; >>> +}; >>> + >>> +enum peci_dimm_threshold_type { >>> +???????temp_max_type, >>> +???????temp_crit_type, >>> +}; >>> + >>> +struct peci_dimmtemp { >>> +???????struct peci_device *peci_dev; >>> +???????struct device *dev; >>> +???????const char *name; >>> +???????const struct dimm_info *gen_info; >>> +???????struct delayed_work detect_work; >>> +???????struct { >>> +???????????????struct peci_sensor_data temp; >>> +???????????????struct peci_dimm_thresholds thresholds; >>> +???????} dimm[DIMM_NUMS_MAX]; >>> +???????char **dimmtemp_label; >>> +???????DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX); >>> +}; >>> + >>> +static u8 __dimm_temp(u32 reg, int dimm_order) >>> +{ >>> +???????return (reg >> (dimm_order * 8)) & 0xff; >>> +} >>> + >>> +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long >>> *val) >>> +{ >>> +???????int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; >>> +???????int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; >>> +???????u32 data; >>> +???????int ret; >> >> ????????int ret = 0; >> >>> + >>> +???????mutex_lock(&priv->dimm[dimm_no].temp.state.lock); >>> +???????if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) >>> +???????????????goto skip_update; >>> + >>> +???????ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, >>> chan_rank, &data); >>> +???????if (ret) { >>> +???????????????mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); >>> +???????????????return ret; >>> +???????} >> >> ????????if (ret) >> ????????????????goto unlock; >> >>> + >>> +???????priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * >>> MILLIDEGREE_PER_DEGREE; >>> + >>> +???????peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state); >>> + >>> +skip_update: >>> +???????*val = priv->dimm[dimm_no].temp.value; >> >> unlock: >>> +???????mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); >>> +???????return 0; >> >> ????????return ret; > > Ack. > >> >>> +} >>> + >>> +static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) >>> +{ >>> +???????int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; >>> +???????int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; >>> +???????u32 data; >>> +???????int ret; >>> + >>> +???????if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state)) >>> +???????????????return 0; >>> + >>> +???????ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, >>> &data); >>> +???????if (ret == -ENODATA) /* Use default or previous value */ >>> +???????????????return 0; >>> +???????if (ret) >>> +???????????????return ret; >>> + >>> +???????priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * >>> MILLIDEGREE_PER_DEGREE; >>> +???????priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * >>> MILLIDEGREE_PER_DEGREE; >>> + >>> +???????peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state); >>> + >>> +???????return 0; >>> +} >>> + >>> +static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum >>> peci_dimm_threshold_type type, >>> +????????????????????????????? int dimm_no, long *val) >>> +{ >>> +???????int ret; >>> + >>> +???????mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); >>> +???????ret = update_thresholds(priv, dimm_no); >>> +???????if (ret) >>> +???????????????goto unlock; >>> + >>> +???????switch (type) { >>> +???????case temp_max_type: >>> +???????????????*val = priv->dimm[dimm_no].thresholds.temp_max; >>> +???????????????break; >>> +???????case temp_crit_type: >>> +???????????????*val = priv->dimm[dimm_no].thresholds.temp_crit; >>> +???????????????break; >>> +???????default: >>> +???????????????ret = -EOPNOTSUPP; >>> +???????????????break; >>> +???????} >>> +unlock: >>> +???????mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); >>> + >>> +???????return ret; >>> +} >>> + >>> +static int dimmtemp_read_string(struct device *dev, >>> +???????????????????????????????enum hwmon_sensor_types type, >>> +???????????????????????????????u32 attr, int channel, const char **str) >>> +{ >>> +???????struct peci_dimmtemp *priv = dev_get_drvdata(dev); >>> + >>> +???????if (attr != hwmon_temp_label) >>> +???????????????return -EOPNOTSUPP; >>> + >>> +???????*str = (const char *)priv->dimmtemp_label[channel]; >>> + >>> +???????return 0; >>> +} >>> + >>> +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, >>> +??????????????????????? u32 attr, int channel, long *val) >>> +{ >>> +???????struct peci_dimmtemp *priv = dev_get_drvdata(dev); >>> + >>> +???????switch (attr) { >>> +???????case hwmon_temp_input: >>> +???????????????return get_dimm_temp(priv, channel, val); >>> +???????case hwmon_temp_max: >>> +???????????????return get_dimm_thresholds(priv, temp_max_type, channel, >>> val); >>> +???????case hwmon_temp_crit: >>> +???????????????return get_dimm_thresholds(priv, temp_crit_type, channel, >>> val); >>> +???????default: >>> +???????????????break; >>> +???????} >>> + >>> +???????return -EOPNOTSUPP; >>> +} >>> + >>> +static umode_t dimmtemp_is_visible(const void *data, enum >>> hwmon_sensor_types type, >>> +????????????????????????????????? u32 attr, int channel) >>> +{ >>> +???????const struct peci_dimmtemp *priv = data; >>> + >>> +???????if (test_bit(channel, priv->dimm_mask)) >>> +???????????????return 0444; >>> + >>> +???????return 0; >>> +} >>> + >>> +static const struct hwmon_ops peci_dimmtemp_ops = { >>> +???????.is_visible = dimmtemp_is_visible, >>> +???????.read_string = dimmtemp_read_string, >>> +???????.read = dimmtemp_read, >>> +}; >>> + >>> +static int check_populated_dimms(struct peci_dimmtemp *priv) >>> +{ >>> +???????int chan_rank_max = priv->gen_info->chan_rank_max; >>> +???????int dimm_idx_max = priv->gen_info->dimm_idx_max; >>> +???????u32 chan_rank_empty = 0; >>> +???????u64 dimm_mask = 0; >>> +???????int chan_rank, dimm_idx, ret; >>> +???????u32 pcs; >>> + >>> +???????BUILD_BUG_ON(CHAN_RANK_MAX > 32); >>> +???????BUILD_BUG_ON(DIMM_NUMS_MAX > 64); >> >> I don't immediately see the value of those build bugs. What happens if >> CHAN_RANK_MAX > 32 or DIMM_NUMS_MAX > 64 ? Where do those limits come >> from ? > > Supported HW doesn't come near the limit for now - it's just an "artificial" > limit imposed by variables we're using (u64 for dimm_mask and u32 for > chan_rank_empty). > Please use a value derived from the size of those variables for the check to clarify and explain the constraints. Thanks, Guenter From zweiss at equinix.com Thu Aug 5 03:52:04 2021 From: zweiss at equinix.com (Zev Weiss) Date: Wed, 4 Aug 2021 17:52:04 +0000 Subject: [PATCH 13/14] docs: hwmon: Document PECI drivers In-Reply-To: References: <20210712220447.957418-1-iwona.winiarska@intel.com> <20210712220447.957418-14-iwona.winiarska@intel.com> <20210727225808.GU8018@packtop> Message-ID: <20210804175203.GY8018@packtop> On Mon, Aug 02, 2021 at 06:37:30AM CDT, Winiarska, Iwona wrote: >On Tue, 2021-07-27 at 22:58 +0000, Zev Weiss wrote: >> On Mon, Jul 12, 2021 at 05:04:46PM CDT, Iwona Winiarska wrote: >> > From: Jae Hyun Yoo >> > >> > Add documentation for peci-cputemp driver that provides DTS thermal >> > readings for CPU packages and CPU cores and peci-dimmtemp driver that >> > provides DTS thermal readings for DIMMs. >> > >> > Signed-off-by: Jae Hyun Yoo >> > Co-developed-by: Iwona Winiarska >> > Signed-off-by: Iwona Winiarska >> > Reviewed-by: Pierre-Louis Bossart >> > --- >> > Documentation/hwmon/index.rst???????? |? 2 + >> > Documentation/hwmon/peci-cputemp.rst? | 93 +++++++++++++++++++++++++++ >> > Documentation/hwmon/peci-dimmtemp.rst | 58 +++++++++++++++++ >> > MAINTAINERS?????????????????????????? |? 2 + >> > 4 files changed, 155 insertions(+) >> > create mode 100644 Documentation/hwmon/peci-cputemp.rst >> > create mode 100644 Documentation/hwmon/peci-dimmtemp.rst >> > >> > diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst >> > index bc01601ea81a..cc76b5b3f791 100644 >> > --- a/Documentation/hwmon/index.rst >> > +++ b/Documentation/hwmon/index.rst >> > @@ -154,6 +154,8 @@ Hardware Monitoring Kernel Drivers >> > ?? pcf8591 >> > ?? pim4328 >> > ?? pm6764tr >> > +?? peci-cputemp >> > +?? peci-dimmtemp >> > ?? pmbus >> > ?? powr1220 >> > ?? pxe1610 >> > diff --git a/Documentation/hwmon/peci-cputemp.rst >> > b/Documentation/hwmon/peci-cputemp.rst >> > new file mode 100644 >> > index 000000000000..d3a218ba810a >> > --- /dev/null >> > +++ b/Documentation/hwmon/peci-cputemp.rst >> > @@ -0,0 +1,93 @@ >> > +.. SPDX-License-Identifier: GPL-2.0-only >> > + >> > +Kernel driver peci-cputemp >> > +========================== >> > + >> > +Supported chips: >> > +???????One of Intel server CPUs listed below which is connected to a PECI >> > bus. >> > +???????????????* Intel Xeon E5/E7 v3 server processors >> > +???????????????????????Intel Xeon E5-14xx v3 family >> > +???????????????????????Intel Xeon E5-24xx v3 family >> > +???????????????????????Intel Xeon E5-16xx v3 family >> > +???????????????????????Intel Xeon E5-26xx v3 family >> > +???????????????????????Intel Xeon E5-46xx v3 family >> > +???????????????????????Intel Xeon E7-48xx v3 family >> > +???????????????????????Intel Xeon E7-88xx v3 family >> > +???????????????* Intel Xeon E5/E7 v4 server processors >> > +???????????????????????Intel Xeon E5-16xx v4 family >> > +???????????????????????Intel Xeon E5-26xx v4 family >> > +???????????????????????Intel Xeon E5-46xx v4 family >> > +???????????????????????Intel Xeon E7-48xx v4 family >> > +???????????????????????Intel Xeon E7-88xx v4 family >> > +???????????????* Intel Xeon Scalable server processors >> > +???????????????????????Intel Xeon D family >> > +???????????????????????Intel Xeon Bronze family >> > +???????????????????????Intel Xeon Silver family >> > +???????????????????????Intel Xeon Gold family >> > +???????????????????????Intel Xeon Platinum family >> > + >> > +???????Datasheet: Available from http://www.intel.com/design/literature.htm >> > + >> > +Author: Jae Hyun Yoo >> > + >> > +Description >> > +----------- >> > + >> > +This driver implements a generic PECI hwmon feature which provides Digital >> > +Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that >> > are >> > +accessible via the processor PECI interface. >> > + >> > +All temperature values are given in millidegree Celsius and will be >> > measurable >> > +only when the target CPU is powered on. >> > + >> > +Sysfs interface >> > +------------------- >> > + >> > +======================= >> > ======================================================= >> > +temp1_label????????????"Die" >> > +temp1_input????????????Provides current die temperature of the CPU package. >> > +temp1_max??????????????Provides thermal control temperature of the CPU >> > package >> > +???????????????????????which is also known as Tcontrol. >> > +temp1_crit?????????????Provides shutdown temperature of the CPU package >> > which >> > +???????????????????????is also known as the maximum processor junction >> > +???????????????????????temperature, Tjmax or Tprochot. >> > +temp1_crit_hyst????????????????Provides the hysteresis value from Tcontrol >> > to Tjmax of >> > +???????????????????????the CPU package. >> > + >> > +temp2_label????????????"DTS" >> > +temp2_input????????????Provides current DTS temperature of the CPU package. >> >> Would this be a good place to note the slightly counter-intuitive nature >> of DTS readings?? i.e. add something along the lines of "The DTS sensor >> produces a delta relative to Tjmax, so negative values are normal and >> values approaching zero are hot."? (In my experience people who aren't >> already familiar with it tend to think something's wrong when a CPU >> temperature reading shows -50C.) > >I believe that what you're referring to is a result of "GetTemp", and we're >using it to calculate "Die" sensor values (temp1). >The sensor value is absolute - we don't expose "raw" thermal sensor value >(delta) anywhere. > >DTS sensor is exposing temperature value scaled to fit DTS 2.0 thermal profile: >https://www.intel.com/content/www/us/en/processors/xeon/scalable/xeon-scalable-thermal-guide.html >(section 5.2.3.2) > >Similar to "Die" sensor - it's also exposed in absolute form. > >I'll try to change description to avoid confusion. > When I tested the patch series by applying it to my OpenBMC kernel, the temp2_input sysfs file produced negative numbers (as has been the case with previous iterations of the PECI patchset). Is that expected? From what Guenter has said it sounds like that's going to need to change so that the temperature readings are all in "normal" millidegrees C (that is, relative to the freezing point of water). Zev From linux at roeck-us.net Thu Aug 5 04:05:48 2021 From: linux at roeck-us.net (Guenter Roeck) Date: Wed, 4 Aug 2021 11:05:48 -0700 Subject: [PATCH 13/14] docs: hwmon: Document PECI drivers In-Reply-To: <20210804175203.GY8018@packtop> References: <20210712220447.957418-1-iwona.winiarska@intel.com> <20210712220447.957418-14-iwona.winiarska@intel.com> <20210727225808.GU8018@packtop> <20210804175203.GY8018@packtop> Message-ID: <4e840768-0315-6241-e988-9a3165a31a86@roeck-us.net> On 8/4/21 10:52 AM, Zev Weiss wrote: > On Mon, Aug 02, 2021 at 06:37:30AM CDT, Winiarska, Iwona wrote: >> On Tue, 2021-07-27 at 22:58 +0000, Zev Weiss wrote: >>> On Mon, Jul 12, 2021 at 05:04:46PM CDT, Iwona Winiarska wrote: >>>> From: Jae Hyun Yoo >>>> >>>> Add documentation for peci-cputemp driver that provides DTS thermal >>>> readings for CPU packages and CPU cores and peci-dimmtemp driver that >>>> provides DTS thermal readings for DIMMs. >>>> >>>> Signed-off-by: Jae Hyun Yoo >>>> Co-developed-by: Iwona Winiarska >>>> Signed-off-by: Iwona Winiarska >>>> Reviewed-by: Pierre-Louis Bossart >>>> --- >>>> Documentation/hwmon/index.rst???????? |? 2 + >>>> Documentation/hwmon/peci-cputemp.rst? | 93 +++++++++++++++++++++++++++ >>>> Documentation/hwmon/peci-dimmtemp.rst | 58 +++++++++++++++++ >>>> MAINTAINERS?????????????????????????? |? 2 + >>>> 4 files changed, 155 insertions(+) >>>> create mode 100644 Documentation/hwmon/peci-cputemp.rst >>>> create mode 100644 Documentation/hwmon/peci-dimmtemp.rst >>>> >>>> diff --git a/Documentation/hwmon/index.rst b/Documentation/hwmon/index.rst >>>> index bc01601ea81a..cc76b5b3f791 100644 >>>> --- a/Documentation/hwmon/index.rst >>>> +++ b/Documentation/hwmon/index.rst >>>> @@ -154,6 +154,8 @@ Hardware Monitoring Kernel Drivers >>>> ?? pcf8591 >>>> ?? pim4328 >>>> ?? pm6764tr >>>> +?? peci-cputemp >>>> +?? peci-dimmtemp >>>> ?? pmbus >>>> ?? powr1220 >>>> ?? pxe1610 >>>> diff --git a/Documentation/hwmon/peci-cputemp.rst >>>> b/Documentation/hwmon/peci-cputemp.rst >>>> new file mode 100644 >>>> index 000000000000..d3a218ba810a >>>> --- /dev/null >>>> +++ b/Documentation/hwmon/peci-cputemp.rst >>>> @@ -0,0 +1,93 @@ >>>> +.. SPDX-License-Identifier: GPL-2.0-only >>>> + >>>> +Kernel driver peci-cputemp >>>> +========================== >>>> + >>>> +Supported chips: >>>> +???????One of Intel server CPUs listed below which is connected to a PECI >>>> bus. >>>> +???????????????* Intel Xeon E5/E7 v3 server processors >>>> +???????????????????????Intel Xeon E5-14xx v3 family >>>> +???????????????????????Intel Xeon E5-24xx v3 family >>>> +???????????????????????Intel Xeon E5-16xx v3 family >>>> +???????????????????????Intel Xeon E5-26xx v3 family >>>> +???????????????????????Intel Xeon E5-46xx v3 family >>>> +???????????????????????Intel Xeon E7-48xx v3 family >>>> +???????????????????????Intel Xeon E7-88xx v3 family >>>> +???????????????* Intel Xeon E5/E7 v4 server processors >>>> +???????????????????????Intel Xeon E5-16xx v4 family >>>> +???????????????????????Intel Xeon E5-26xx v4 family >>>> +???????????????????????Intel Xeon E5-46xx v4 family >>>> +???????????????????????Intel Xeon E7-48xx v4 family >>>> +???????????????????????Intel Xeon E7-88xx v4 family >>>> +???????????????* Intel Xeon Scalable server processors >>>> +???????????????????????Intel Xeon D family >>>> +???????????????????????Intel Xeon Bronze family >>>> +???????????????????????Intel Xeon Silver family >>>> +???????????????????????Intel Xeon Gold family >>>> +???????????????????????Intel Xeon Platinum family >>>> + >>>> +???????Datasheet: Available from http://www.intel.com/design/literature.htm >>>> + >>>> +Author: Jae Hyun Yoo >>>> + >>>> +Description >>>> +----------- >>>> + >>>> +This driver implements a generic PECI hwmon feature which provides Digital >>>> +Thermal Sensor (DTS) thermal readings of the CPU package and CPU cores that >>>> are >>>> +accessible via the processor PECI interface. >>>> + >>>> +All temperature values are given in millidegree Celsius and will be >>>> measurable >>>> +only when the target CPU is powered on. >>>> + >>>> +Sysfs interface >>>> +------------------- >>>> + >>>> +======================= >>>> ======================================================= >>>> +temp1_label????????????"Die" >>>> +temp1_input????????????Provides current die temperature of the CPU package. >>>> +temp1_max??????????????Provides thermal control temperature of the CPU >>>> package >>>> +???????????????????????which is also known as Tcontrol. >>>> +temp1_crit?????????????Provides shutdown temperature of the CPU package >>>> which >>>> +???????????????????????is also known as the maximum processor junction >>>> +???????????????????????temperature, Tjmax or Tprochot. >>>> +temp1_crit_hyst????????????????Provides the hysteresis value from Tcontrol >>>> to Tjmax of >>>> +???????????????????????the CPU package. >>>> + >>>> +temp2_label????????????"DTS" >>>> +temp2_input????????????Provides current DTS temperature of the CPU package. >>> >>> Would this be a good place to note the slightly counter-intuitive nature >>> of DTS readings?? i.e. add something along the lines of "The DTS sensor >>> produces a delta relative to Tjmax, so negative values are normal and >>> values approaching zero are hot."? (In my experience people who aren't >>> already familiar with it tend to think something's wrong when a CPU >>> temperature reading shows -50C.) >> >> I believe that what you're referring to is a result of "GetTemp", and we're >> using it to calculate "Die" sensor values (temp1). >> The sensor value is absolute - we don't expose "raw" thermal sensor value >> (delta) anywhere. >> >> DTS sensor is exposing temperature value scaled to fit DTS 2.0 thermal profile: >> https://www.intel.com/content/www/us/en/processors/xeon/scalable/xeon-scalable-thermal-guide.html >> (section 5.2.3.2) >> >> Similar to "Die" sensor - it's also exposed in absolute form. >> >> I'll try to change description to avoid confusion. >> > > When I tested the patch series by applying it to my OpenBMC kernel, the > temp2_input sysfs file produced negative numbers (as has been the case > with previous iterations of the PECI patchset). Is that expected? From > what Guenter has said it sounds like that's going to need to change so > that the temperature readings are all in "normal" millidegrees C > (that is, relative to the freezing point of water). > Correct, the temperature is expected to be reported in millidegrees C per hwmon ABI. Everything else is unacceptable. That makes me wonder what "raw" and "absolute" means. Negative numbers suggest that, whatever is reported today, it is not millidegrees C. Guenter From joel at jms.id.au Thu Aug 5 10:28:02 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 5 Aug 2021 00:28:02 +0000 Subject: [PATCH 6/6] ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC In-Reply-To: <20210728233755.17963-7-rentao.bupt@gmail.com> References: <20210728233755.17963-1-rentao.bupt@gmail.com> <20210728233755.17963-7-rentao.bupt@gmail.com> Message-ID: On Wed, 28 Jul 2021 at 23:38, wrote: > > From: Tao Ren > > Add initial version of device tree for Facebook Fuji (AST2600) BMC. I like to read what kind of platform the BMC is going into if you can add that detail, but it's not essential. > +&spi1 { > + status = "okay"; > + > + /* > + * Customize spi1 flash memory size to 32MB (maximum flash size on > + * the bus) to save vmalloc space. > + */ > + reg = < 0x1e630000 0xc4 > + 0x30000000 0x2000000 >; Which driver supports this? It would be great to see Facebook work to get the SPI NOR driver for the ast2600 merged to mainline. I doubt the IBM team will get to this, as we are using eMMC instead. > + > + flash at 0 { > + status = "okay"; > + m25p,fast-read; > + label = "spi1.0"; > + spi-max-frequency = <5000000>; > + > + partitions { > + compatible = "fixed-partitions"; > + #address-cells = <1>; > + #size-cells = <1>; > + > + flash1 at 0 { > + reg = <0x0 0x2000000>; > + label = "system-flash"; > + }; > + }; > + }; > +}; > +&ehci1 { > + status = "okay"; > +}; Have you verified that USB works with mainline? I've had reports of it working on 5.8 but it seems to have regressed as of v5.10. > +&mdio1 { > + status = "okay"; > + > + ethphy3: ethernet-phy at 13 { > + compatible = "ethernet-phy-ieee802.3-c22"; > + reg = <0x0d>; > + }; > +}; > + > +&mac3 { > + phy-handle = <ðphy3>; status = okay? You should specify the pinmux too I think, even if the default happens to work, so that other devices cannot claim the pins. Cheers, Joel From rentao.bupt at gmail.com Thu Aug 5 11:19:51 2021 From: rentao.bupt at gmail.com (Tao Ren) Date: Wed, 4 Aug 2021 18:19:51 -0700 Subject: [PATCH 6/6] ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC In-Reply-To: References: <20210728233755.17963-1-rentao.bupt@gmail.com> <20210728233755.17963-7-rentao.bupt@gmail.com> Message-ID: <20210805011951.GA28444@taoren-ubuntu-R90MNF91> On Thu, Aug 05, 2021 at 12:28:02AM +0000, Joel Stanley wrote: > On Wed, 28 Jul 2021 at 23:38, wrote: > > > > From: Tao Ren > > > > Add initial version of device tree for Facebook Fuji (AST2600) BMC. > > I like to read what kind of platform the BMC is going into if you can > add that detail, but it's not essential. Sure. I will add more details in v2. > > +&spi1 { > > + status = "okay"; > > + > > + /* > > + * Customize spi1 flash memory size to 32MB (maximum flash size on > > + * the bus) to save vmalloc space. > > + */ > > + reg = < 0x1e630000 0xc4 > > + 0x30000000 0x2000000 >; > > Which driver supports this? > > It would be great to see Facebook work to get the SPI NOR driver for > the ast2600 merged to mainline. > > I doubt the IBM team will get to this, as we are using eMMC instead. Ah, I just checked aspeed-g6.dtsi (ast2600-spi) in mainline and I thought the driver patches were also upstreamed. Let me remove the entry for now, and will add it back when the driver is ready. > > > + > > + flash at 0 { > > + status = "okay"; > > + m25p,fast-read; > > + label = "spi1.0"; > > + spi-max-frequency = <5000000>; > > + > > + partitions { > > + compatible = "fixed-partitions"; > > + #address-cells = <1>; > > + #size-cells = <1>; > > + > > + flash1 at 0 { > > + reg = <0x0 0x2000000>; > > + label = "system-flash"; > > + }; > > + }; > > + }; > > +}; > > > +&ehci1 { > > + status = "okay"; > > +}; > > Have you verified that USB works with mainline? I've had reports of it > working on 5.8 but it seems to have regressed as of v5.10. It stopped working on ASPEED since commit 280a9045bb18 ("ehci: fix EHCI host controller initialization sequence"): ehci_handshake() returns error because HCHalted bit EHCI24[12] stays at 1. I have a dirty hack in my tree (ignoring the halt bit) and it "works" on AST2500 and AST2600. Let me send an email to openbmc and aspeed email groups to see if anyone has more context. Meanwhile, should I delete the entry until the fix is ready in driver? > > +&mdio1 { > > + status = "okay"; > > + > > + ethphy3: ethernet-phy at 13 { > > + compatible = "ethernet-phy-ieee802.3-c22"; > > + reg = <0x0d>; > > + }; > > +}; > > + > > +&mac3 { > > + phy-handle = <ðphy3>; > > status = okay? > > You should specify the pinmux too I think, even if the default happens > to work, so that other devices cannot claim the pins. status is set in ast2600 common dtsi, but let me set it here to avoid confusion. Will update pinmux in v2. Thanks. > > Cheers, > > Joel Cheers, Tao From joel at jms.id.au Thu Aug 5 11:37:29 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 5 Aug 2021 01:37:29 +0000 Subject: [PATCH 6/6] ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC In-Reply-To: <20210805011951.GA28444@taoren-ubuntu-R90MNF91> References: <20210728233755.17963-1-rentao.bupt@gmail.com> <20210728233755.17963-7-rentao.bupt@gmail.com> <20210805011951.GA28444@taoren-ubuntu-R90MNF91> Message-ID: On Thu, 5 Aug 2021 at 01:20, Tao Ren wrote: > > On Thu, Aug 05, 2021 at 12:28:02AM +0000, Joel Stanley wrote: > > On Wed, 28 Jul 2021 at 23:38, wrote: > > > > > > From: Tao Ren > > > > > > Add initial version of device tree for Facebook Fuji (AST2600) BMC. > > > > I like to read what kind of platform the BMC is going into if you can > > add that detail, but it's not essential. > > Sure. I will add more details in v2. > > > > +&spi1 { > > > + status = "okay"; > > > + > > > + /* > > > + * Customize spi1 flash memory size to 32MB (maximum flash size on > > > + * the bus) to save vmalloc space. > > > + */ > > > + reg = < 0x1e630000 0xc4 > > > + 0x30000000 0x2000000 >; > > > > Which driver supports this? > > > > It would be great to see Facebook work to get the SPI NOR driver for > > the ast2600 merged to mainline. > > > > I doubt the IBM team will get to this, as we are using eMMC instead. > > Ah, I just checked aspeed-g6.dtsi (ast2600-spi) in mainline and I thought > the driver patches were also upstreamed. Let me remove the entry for now, > and will add it back when the driver is ready. If it helps you, I don't mind it staying in your dts. I would like to see some effort on the driver though. > > > > > > + > > > + flash at 0 { > > > + status = "okay"; > > > + m25p,fast-read; > > > + label = "spi1.0"; > > > + spi-max-frequency = <5000000>; > > > + > > > + partitions { > > > + compatible = "fixed-partitions"; > > > + #address-cells = <1>; > > > + #size-cells = <1>; > > > + > > > + flash1 at 0 { > > > + reg = <0x0 0x2000000>; > > > + label = "system-flash"; > > > + }; > > > + }; > > > + }; > > > +}; > > > > > +&ehci1 { > > > + status = "okay"; > > > +}; > > > > Have you verified that USB works with mainline? I've had reports of it > > working on 5.8 but it seems to have regressed as of v5.10. > > It stopped working on ASPEED since commit 280a9045bb18 ("ehci: fix EHCI > host controller initialization sequence"): ehci_handshake() returns > error because HCHalted bit EHCI24[12] stays at 1. > > I have a dirty hack in my tree (ignoring the halt bit) and it "works" on > AST2500 and AST2600. Let me send an email to openbmc and aspeed email > groups to see if anyone has more context. We discovered the same, except in my testing it wasn't actually "working" on my EVB. I have a A3 EVB on my desk and when plugging in a USB key nothing happened. > > Meanwhile, should I delete the entry until the fix is ready in driver? Again, leave it in I think. > > > > +&mdio1 { > > > + status = "okay"; > > > + > > > + ethphy3: ethernet-phy at 13 { > > > + compatible = "ethernet-phy-ieee802.3-c22"; > > > + reg = <0x0d>; > > > + }; > > > +}; > > > + > > > +&mac3 { > > > + phy-handle = <ðphy3>; > > > > status = okay? > > > > You should specify the pinmux too I think, even if the default happens > > to work, so that other devices cannot claim the pins. > > status is set in ast2600 common dtsi, but let me set it here to avoid > confusion. Will update pinmux in v2. Thanks. Are you looking at mainline? I think it's disabled by default there: https://elixir.bootlin.com/linux/latest/source/arch/arm/boot/dts/aspeed-g6.dtsi#L246 > > > > > Cheers, > > > > Joel > > Cheers, > > Tao From rentao.bupt at gmail.com Thu Aug 5 12:09:46 2021 From: rentao.bupt at gmail.com (Tao Ren) Date: Wed, 4 Aug 2021 19:09:46 -0700 Subject: [PATCH 6/6] ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC In-Reply-To: References: <20210728233755.17963-1-rentao.bupt@gmail.com> <20210728233755.17963-7-rentao.bupt@gmail.com> <20210805011951.GA28444@taoren-ubuntu-R90MNF91> Message-ID: <20210805020946.GA29371@taoren-ubuntu-R90MNF91> On Thu, Aug 05, 2021 at 01:37:29AM +0000, Joel Stanley wrote: > On Thu, 5 Aug 2021 at 01:20, Tao Ren wrote: > > > > On Thu, Aug 05, 2021 at 12:28:02AM +0000, Joel Stanley wrote: > > > On Wed, 28 Jul 2021 at 23:38, wrote: > > > > > > > > From: Tao Ren > > > > > > > > Add initial version of device tree for Facebook Fuji (AST2600) BMC. > > > > > > I like to read what kind of platform the BMC is going into if you can > > > add that detail, but it's not essential. > > > > Sure. I will add more details in v2. > > > > > > +&spi1 { > > > > + status = "okay"; > > > > + > > > > + /* > > > > + * Customize spi1 flash memory size to 32MB (maximum flash size on > > > > + * the bus) to save vmalloc space. > > > > + */ > > > > + reg = < 0x1e630000 0xc4 > > > > + 0x30000000 0x2000000 >; > > > > > > Which driver supports this? > > > > > > It would be great to see Facebook work to get the SPI NOR driver for > > > the ast2600 merged to mainline. > > > > > > I doubt the IBM team will get to this, as we are using eMMC instead. > > > > Ah, I just checked aspeed-g6.dtsi (ast2600-spi) in mainline and I thought > > the driver patches were also upstreamed. Let me remove the entry for now, > > and will add it back when the driver is ready. > > If it helps you, I don't mind it staying in your dts. I would like to > see some effort on the driver though. > > > > > > > > > > + > > > > + flash at 0 { > > > > + status = "okay"; > > > > + m25p,fast-read; > > > > + label = "spi1.0"; > > > > + spi-max-frequency = <5000000>; > > > > + > > > > + partitions { > > > > + compatible = "fixed-partitions"; > > > > + #address-cells = <1>; > > > > + #size-cells = <1>; > > > > + > > > > + flash1 at 0 { > > > > + reg = <0x0 0x2000000>; > > > > + label = "system-flash"; > > > > + }; > > > > + }; > > > > + }; > > > > +}; > > > > > > > +&ehci1 { > > > > + status = "okay"; > > > > +}; > > > > > > Have you verified that USB works with mainline? I've had reports of it > > > working on 5.8 but it seems to have regressed as of v5.10. > > > > It stopped working on ASPEED since commit 280a9045bb18 ("ehci: fix EHCI > > host controller initialization sequence"): ehci_handshake() returns > > error because HCHalted bit EHCI24[12] stays at 1. > > > > I have a dirty hack in my tree (ignoring the halt bit) and it "works" on > > AST2500 and AST2600. Let me send an email to openbmc and aspeed email > > groups to see if anyone has more context. > > We discovered the same, except in my testing it wasn't actually > "working" on my EVB. I have a A3 EVB on my desk and when plugging in a > USB key nothing happened. We have a non-removable USB device on Fuji (AST2600) BMC and it can be enumerated successfully (after applying the hack). Let me vist the lab and plug a USB key to CMM (AST2500) BMC sometime next week. Will collect results and send out email then. > > > > > Meanwhile, should I delete the entry until the fix is ready in driver? > > Again, leave it in I think. > > > > > > > +&mdio1 { > > > > + status = "okay"; > > > > + > > > > + ethphy3: ethernet-phy at 13 { > > > > + compatible = "ethernet-phy-ieee802.3-c22"; > > > > + reg = <0x0d>; > > > > + }; > > > > +}; > > > > + > > > > +&mac3 { > > > > + phy-handle = <ðphy3>; > > > > > > status = okay? > > > > > > You should specify the pinmux too I think, even if the default happens > > > to work, so that other devices cannot claim the pins. > > > > status is set in ast2600 common dtsi, but let me set it here to avoid > > confusion. Will update pinmux in v2. Thanks. > > Are you looking at mainline? I think it's disabled by default there: > > https://elixir.bootlin.com/linux/latest/source/arch/arm/boot/dts/aspeed-g6.dtsi#L246 Sorry for the confusion, Joel. I mean the device is enabled in "ast2600-facebook-netbmc-common.dtsi" (patch #3 in the series). Do we still need to set status to "okay" explicitly in platform dts (to avoid confusion)? &mac3 { status = "okay"; phy-mode = "rgmii"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_rgmii4_default>; }; > > > > > > > > > Cheers, > > > > > > Joel > > > > Cheers, > > > > Tao From joel at jms.id.au Thu Aug 5 12:17:13 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 5 Aug 2021 02:17:13 +0000 Subject: [PATCH 6/6] ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC In-Reply-To: <20210805020946.GA29371@taoren-ubuntu-R90MNF91> References: <20210728233755.17963-1-rentao.bupt@gmail.com> <20210728233755.17963-7-rentao.bupt@gmail.com> <20210805011951.GA28444@taoren-ubuntu-R90MNF91> <20210805020946.GA29371@taoren-ubuntu-R90MNF91> Message-ID: On Thu, 5 Aug 2021 at 02:09, Tao Ren wrote: > > On Thu, Aug 05, 2021 at 01:37:29AM +0000, Joel Stanley wrote: > > On Thu, 5 Aug 2021 at 01:20, Tao Ren wrote: > > > > > +&ehci1 { > > > > > + status = "okay"; > > > > > +}; > > > > > > > > Have you verified that USB works with mainline? I've had reports of it > > > > working on 5.8 but it seems to have regressed as of v5.10. > > > > > > It stopped working on ASPEED since commit 280a9045bb18 ("ehci: fix EHCI > > > host controller initialization sequence"): ehci_handshake() returns > > > error because HCHalted bit EHCI24[12] stays at 1. > > > > > > I have a dirty hack in my tree (ignoring the halt bit) and it "works" on > > > AST2500 and AST2600. Let me send an email to openbmc and aspeed email > > > groups to see if anyone has more context. > > > > We discovered the same, except in my testing it wasn't actually > > "working" on my EVB. I have a A3 EVB on my desk and when plugging in a > > USB key nothing happened. > > We have a non-removable USB device on Fuji (AST2600) BMC and it can be > enumerated successfully (after applying the hack). Let me vist the lab > and plug a USB key to CMM (AST2500) BMC sometime next week. Will collect > results and send out email then. Thanks! > > > status is set in ast2600 common dtsi, but let me set it here to avoid > > > confusion. Will update pinmux in v2. Thanks. > > > > Are you looking at mainline? I think it's disabled by default there: > > > > https://elixir.bootlin.com/linux/latest/source/arch/arm/boot/dts/aspeed-g6.dtsi#L246 > > Sorry for the confusion, Joel. I mean the device is enabled in > "ast2600-facebook-netbmc-common.dtsi" (patch #3 in the series). Do we > still need to set status to "okay" explicitly in platform dts (to avoid > confusion)? I see, thanks for explaining. It's a downside of the device tree bindings for the ethernet device. Given you will be the one working with the board, I think it's up to you how you lay it out. You could choose to add a comment to the platform dts, or put the hunk you currently have in the common.dtsi in the platform. > > &mac3 { > status = "okay"; > phy-mode = "rgmii"; > pinctrl-names = "default"; > pinctrl-0 = <&pinctrl_rgmii4_default>; > }; From gregkh at linuxfoundation.org Thu Aug 5 22:17:57 2021 From: gregkh at linuxfoundation.org (Greg Kroah-Hartman) Date: Thu, 5 Aug 2021 14:17:57 +0200 Subject: [PATCH v2 00/15] Introduce PECI subsystem In-Reply-To: <20210803113134.2262882-1-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> Message-ID: On Tue, Aug 03, 2021 at 01:31:19PM +0200, Iwona Winiarska wrote: > Hi Greg, > > This is a second round of patches introducing PECI subsystem. > I don't think it is ready to be applied right away (we're still > missing r-b's), but I hope we have chance to complete discussion in > the 5.15 development cycle. I would appreciate if you could take > a look. I will wait to review this when you all feel it is ready so as to not waste my time finding things that you already know need to be resolved. thanks, greg k-h From bgolaszewski at baylibre.com Fri Aug 6 05:17:20 2021 From: bgolaszewski at baylibre.com (Bartosz Golaszewski) Date: Thu, 5 Aug 2021 21:17:20 +0200 Subject: [PATCH v6 0/9] ASPEED sgpio driver enhancement. In-Reply-To: References: <20210712100317.23298-1-steven_lee@aspeedtech.com> <20210723031615.GA10457@aspeedtech.com> <58256e8f-6c9a-4ad4-b51e-4048b6feb42a@www.fastmail.com> Message-ID: On Tue, Aug 3, 2021 at 7:58 AM Joel Stanley wrote: > > On Tue, 3 Aug 2021 at 04:49, Andrew Jeffery wrote: > > On Fri, 23 Jul 2021, at 17:00, Bartosz Golaszewski wrote: > > > Joel, Andrew: can I take the GPIO patches through the GPIO tree and > > > you'll take the ARM patches separately into v5.15? > > > > I think that should be okay. I'll poke Joel. > > Yes, that's fine. I have merged the first four patches into the aspeed tree. > > Cheers, > > Joel Thanks, I applied patches 5-9 to the GPIO tree. Bart From iwona.winiarska at intel.com Fri Aug 6 07:42:04 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Thu, 5 Aug 2021 21:42:04 +0000 Subject: [PATCH 13/14] docs: hwmon: Document PECI drivers In-Reply-To: <4e840768-0315-6241-e988-9a3165a31a86@roeck-us.net> References: <20210712220447.957418-1-iwona.winiarska@intel.com> <20210712220447.957418-14-iwona.winiarska@intel.com> <20210727225808.GU8018@packtop> <20210804175203.GY8018@packtop> <4e840768-0315-6241-e988-9a3165a31a86@roeck-us.net> Message-ID: <6891496eabcc6f9cacec4fea505fb757ea9c11fc.camel@intel.com> On Wed, 2021-08-04 at 11:05 -0700, Guenter Roeck wrote: > On 8/4/21 10:52 AM, Zev Weiss wrote: > > On Mon, Aug 02, 2021 at 06:37:30AM CDT, Winiarska, Iwona wrote: > > > On Tue, 2021-07-27 at 22:58 +0000, Zev Weiss wrote: > > > > On Mon, Jul 12, 2021 at 05:04:46PM CDT, Iwona Winiarska wrote: > > > > > From: Jae Hyun Yoo > > > > > > > > > > Add documentation for peci-cputemp driver that provides DTS thermal > > > > > readings for CPU packages and CPU cores and peci-dimmtemp driver that > > > > > provides DTS thermal readings for DIMMs. > > > > > > > > > > Signed-off-by: Jae Hyun Yoo > > > > > Co-developed-by: Iwona Winiarska > > > > > Signed-off-by: Iwona Winiarska > > > > > Reviewed-by: Pierre-Louis Bossart > > > > > > > > > > --- > > > > > Documentation/hwmon/index.rst???????? |? 2 + > > > > > Documentation/hwmon/peci-cputemp.rst? | 93 +++++++++++++++++++++++++++ > > > > > Documentation/hwmon/peci-dimmtemp.rst | 58 +++++++++++++++++ > > > > > MAINTAINERS?????????????????????????? |? 2 + > > > > > 4 files changed, 155 insertions(+) > > > > > create mode 100644 Documentation/hwmon/peci-cputemp.rst > > > > > create mode 100644 Documentation/hwmon/peci-dimmtemp.rst > > > > > > > > > > diff --git a/Documentation/hwmon/index.rst > > > > > b/Documentation/hwmon/index.rst > > > > > index bc01601ea81a..cc76b5b3f791 100644 > > > > > --- a/Documentation/hwmon/index.rst > > > > > +++ b/Documentation/hwmon/index.rst > > > > > @@ -154,6 +154,8 @@ Hardware Monitoring Kernel Drivers > > > > > ??? pcf8591 > > > > > ??? pim4328 > > > > > ??? pm6764tr > > > > > +?? peci-cputemp > > > > > +?? peci-dimmtemp > > > > > ??? pmbus > > > > > ??? powr1220 > > > > > ??? pxe1610 > > > > > diff --git a/Documentation/hwmon/peci-cputemp.rst > > > > > b/Documentation/hwmon/peci-cputemp.rst > > > > > new file mode 100644 > > > > > index 000000000000..d3a218ba810a > > > > > --- /dev/null > > > > > +++ b/Documentation/hwmon/peci-cputemp.rst > > > > > @@ -0,0 +1,93 @@ > > > > > +.. SPDX-License-Identifier: GPL-2.0-only > > > > > + > > > > > +Kernel driver peci-cputemp > > > > > +========================== > > > > > + > > > > > +Supported chips: > > > > > +???????One of Intel server CPUs listed below which is connected to a > > > > > PECI > > > > > bus. > > > > > +???????????????* Intel Xeon E5/E7 v3 server processors > > > > > +???????????????????????Intel Xeon E5-14xx v3 family > > > > > +???????????????????????Intel Xeon E5-24xx v3 family > > > > > +???????????????????????Intel Xeon E5-16xx v3 family > > > > > +???????????????????????Intel Xeon E5-26xx v3 family > > > > > +???????????????????????Intel Xeon E5-46xx v3 family > > > > > +???????????????????????Intel Xeon E7-48xx v3 family > > > > > +???????????????????????Intel Xeon E7-88xx v3 family > > > > > +???????????????* Intel Xeon E5/E7 v4 server processors > > > > > +???????????????????????Intel Xeon E5-16xx v4 family > > > > > +???????????????????????Intel Xeon E5-26xx v4 family > > > > > +???????????????????????Intel Xeon E5-46xx v4 family > > > > > +???????????????????????Intel Xeon E7-48xx v4 family > > > > > +???????????????????????Intel Xeon E7-88xx v4 family > > > > > +???????????????* Intel Xeon Scalable server processors > > > > > +???????????????????????Intel Xeon D family > > > > > +???????????????????????Intel Xeon Bronze family > > > > > +???????????????????????Intel Xeon Silver family > > > > > +???????????????????????Intel Xeon Gold family > > > > > +???????????????????????Intel Xeon Platinum family > > > > > + > > > > > +???????Datasheet: Available from > > > > > http://www.intel.com/design/literature.htm > > > > > + > > > > > +Author: Jae Hyun Yoo > > > > > + > > > > > +Description > > > > > +----------- > > > > > + > > > > > +This driver implements a generic PECI hwmon feature which provides > > > > > Digital > > > > > +Thermal Sensor (DTS) thermal readings of the CPU package and CPU > > > > > cores that > > > > > are > > > > > +accessible via the processor PECI interface. > > > > > + > > > > > +All temperature values are given in millidegree Celsius and will be > > > > > measurable > > > > > +only when the target CPU is powered on. > > > > > + > > > > > +Sysfs interface > > > > > +------------------- > > > > > + > > > > > +======================= > > > > > ======================================================= > > > > > +temp1_label????????????"Die" > > > > > +temp1_input????????????Provides current die temperature of the CPU > > > > > package. > > > > > +temp1_max??????????????Provides thermal control temperature of the > > > > > CPU > > > > > package > > > > > +???????????????????????which is also known as Tcontrol. > > > > > +temp1_crit?????????????Provides shutdown temperature of the CPU > > > > > package > > > > > which > > > > > +???????????????????????is also known as the maximum processor > > > > > junction > > > > > +???????????????????????temperature, Tjmax or Tprochot. > > > > > +temp1_crit_hyst????????????????Provides the hysteresis value from > > > > > Tcontrol > > > > > to Tjmax of > > > > > +???????????????????????the CPU package. > > > > > + > > > > > +temp2_label????????????"DTS" > > > > > +temp2_input????????????Provides current DTS temperature of the CPU > > > > > package. > > > > > > > > Would this be a good place to note the slightly counter-intuitive nature > > > > of DTS readings?? i.e. add something along the lines of "The DTS sensor > > > > produces a delta relative to Tjmax, so negative values are normal and > > > > values approaching zero are hot."? (In my experience people who aren't > > > > already familiar with it tend to think something's wrong when a CPU > > > > temperature reading shows -50C.) > > > > > > I believe that what you're referring to is a result of "GetTemp", and > > > we're > > > using it to calculate "Die" sensor values (temp1). > > > The sensor value is absolute - we don't expose "raw" thermal sensor value > > > (delta) anywhere. > > > > > > DTS sensor is exposing temperature value scaled to fit DTS 2.0 thermal > > > profile: > > > https://www.intel.com/content/www/us/en/processors/xeon/scalable/xeon-scalable-thermal-guide.html > > > (section 5.2.3.2) > > > > > > Similar to "Die" sensor - it's also exposed in absolute form. > > > > > > I'll try to change description to avoid confusion. > > > > > > > When I tested the patch series by applying it to my OpenBMC kernel, the > > temp2_input sysfs file produced negative numbers (as has been the case > > with previous iterations of the PECI patchset).? Is that expected?? From > > what Guenter has said it sounds like that's going to need to change so > > that the temperature readings are all in "normal" millidegrees C > > (that is, relative to the freezing point of water). > > > > Correct, the temperature is expected to be reported in millidegrees C > per hwmon ABI. Everything else is unacceptable. That makes me wonder what > "raw" and "absolute" means. Negative numbers suggest that, whatever is > reported today, it is not millidegrees C. Let's say we have two values: "base" and "delta". Both are in milidegrees C. "absolute" means that the sensor value exposed to userspace is calculated as: base - delta (or base + delta, depending on sensor). "relative" would mean that we expose "delta" to userspace as sensor value. For peci-cputemp (and dimmtemp) we're exposing sensors in "absolute" form. I contacted Zev and we found that the platform he uses has a different format for the "raw" value ("delta" in the example above) of this particular sensor (S8.8 instead of S10.6), which means that we're subtracting significantly larger number than we should, resulting in sensor going into negative. On the platform I'm using for development purpose, sampling Die and DTS values returned: Die 26344 DTS 26329 The platform that Zev used is currently not supported by peci-cpu, however, I went through the specs, and it looks like some of the older supported platforms are also using S8.8. I'll fix this in v3. Thanks -Iwona > > Guenter From iwona.winiarska at intel.com Fri Aug 6 07:48:31 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Thu, 5 Aug 2021 21:48:31 +0000 Subject: [PATCH v2 13/15] hwmon: peci: Add dimmtemp driver In-Reply-To: References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-14-iwona.winiarska@intel.com> <20210803153937.GA337938@roeck-us.net> Message-ID: <941c59624b65c727f828b2b30a8b64c3067ff7c9.camel@intel.com> On Wed, 2021-08-04 at 10:33 -0700, Guenter Roeck wrote: > On 8/4/21 3:46 AM, Winiarska, Iwona wrote: > > On Tue, 2021-08-03 at 08:39 -0700, Guenter Roeck wrote: > > > On Tue, Aug 03, 2021 at 01:31:32PM +0200, Iwona Winiarska wrote: > > > > Add peci-dimmtemp driver for Temperature Sensor on DIMM readings that > > > > are accessible via the processor PECI interface. > > > > > > > > The main use case for the driver (and PECI interface) is out-of-band > > > > management, where we're able to obtain thermal readings from an external > > > > entity connected with PECI, e.g. BMC on server platforms. > > > > > > > > Co-developed-by: Jae Hyun Yoo > > > > Signed-off-by: Jae Hyun Yoo > > > > Signed-off-by: Iwona Winiarska > > > > Reviewed-by: Pierre-Louis Bossart > > > > --- > > > > Note that the timeout was completely removed - we're going to probe > > > > for detected DIMMs every 5 seconds until we reach "stable" state of > > > > either getting correct DIMM data or getting all -EINVAL (which > > > > suggest that the CPU doesn't have any DIMMs). > > > > > > > > ??drivers/hwmon/peci/Kconfig??? |? 13 + > > > > ??drivers/hwmon/peci/Makefile?? |?? 2 + > > > > ??drivers/hwmon/peci/dimmtemp.c | 614 ++++++++++++++++++++++++++++++++++ > > > > ??3 files changed, 629 insertions(+) > > > > ??create mode 100644 drivers/hwmon/peci/dimmtemp.c > > > > > > > > diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig > > > > index e10eed68d70a..9d32a57badfe 100644 > > > > --- a/drivers/hwmon/peci/Kconfig > > > > +++ b/drivers/hwmon/peci/Kconfig > > > > @@ -14,5 +14,18 @@ config SENSORS_PECI_CPUTEMP > > > > ?????????? This driver can also be built as a module. If so, the module > > > > ?????????? will be called peci-cputemp. > > > > ? > > > > +config SENSORS_PECI_DIMMTEMP > > > > +???????tristate "PECI DIMM temperature monitoring client" > > > > +???????depends on PECI > > > > +???????select SENSORS_PECI > > > > +???????select PECI_CPU > > > > +???????help > > > > +???????? If you say yes here you get support for the generic Intel PECI > > > > hwmon > > > > +???????? driver which provides Temperature Sensor on DIMM readings that > > > > are > > > > +???????? accessible via the processor PECI interface. > > > > + > > > > +???????? This driver can also be built as a module. If so, the module > > > > +???????? will be called peci-dimmtemp. > > > > + > > > > ??config SENSORS_PECI > > > > ?????????tristate > > > > diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile > > > > index e8a0ada5ab1f..191cfa0227f3 100644 > > > > --- a/drivers/hwmon/peci/Makefile > > > > +++ b/drivers/hwmon/peci/Makefile > > > > @@ -1,5 +1,7 @@ > > > > ??# SPDX-License-Identifier: GPL-2.0-only > > > > ? > > > > ??peci-cputemp-y := cputemp.o > > > > +peci-dimmtemp-y := dimmtemp.o > > > > ? > > > > ??obj-$(CONFIG_SENSORS_PECI_CPUTEMP)?????+= peci-cputemp.o > > > > +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)????+= peci-dimmtemp.o > > > > diff --git a/drivers/hwmon/peci/dimmtemp.c > > > > b/drivers/hwmon/peci/dimmtemp.c > > > > new file mode 100644 > > > > index 000000000000..6264c29bb6c0 > > > > --- /dev/null > > > > +++ b/drivers/hwmon/peci/dimmtemp.c > > > > @@ -0,0 +1,614 @@ > > > > +// SPDX-License-Identifier: GPL-2.0-only > > > > +// Copyright (c) 2018-2021 Intel Corporation > > > > + > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > +#include > > > > + > > > > +#include "common.h" > > > > + > > > > +#define DIMM_MASK_CHECK_DELAY_JIFFIES??msecs_to_jiffies(5000) > > > > + > > > > +/* Max number of channel ranks and DIMM index per channel */ > > > > +#define CHAN_RANK_MAX_ON_HSX???8 > > > > +#define DIMM_IDX_MAX_ON_HSX????3 > > > > +#define CHAN_RANK_MAX_ON_BDX???4 > > > > +#define DIMM_IDX_MAX_ON_BDX????3 > > > > +#define CHAN_RANK_MAX_ON_BDXD??2 > > > > +#define DIMM_IDX_MAX_ON_BDXD???2 > > > > +#define CHAN_RANK_MAX_ON_SKX???6 > > > > +#define DIMM_IDX_MAX_ON_SKX????2 > > > > +#define CHAN_RANK_MAX_ON_ICX???8 > > > > +#define DIMM_IDX_MAX_ON_ICX????2 > > > > +#define CHAN_RANK_MAX_ON_ICXD??4 > > > > +#define DIMM_IDX_MAX_ON_ICXD???2 > > > > + > > > > +#define CHAN_RANK_MAX??????????CHAN_RANK_MAX_ON_HSX > > > > +#define DIMM_IDX_MAX???????????DIMM_IDX_MAX_ON_HSX > > > > +#define DIMM_NUMS_MAX??????????(CHAN_RANK_MAX * DIMM_IDX_MAX) > > > > + > > > > +#define CPU_SEG_MASK???????????GENMASK(23, 16) > > > > +#define GET_CPU_SEG(x)?????????(((x) & CPU_SEG_MASK) >> 16) > > > > +#define CPU_BUS_MASK???????????GENMASK(7, 0) > > > > +#define GET_CPU_BUS(x)?????????((x) & CPU_BUS_MASK) > > > > + > > > > +#define DIMM_TEMP_MAX??????????GENMASK(15, 8) > > > > +#define DIMM_TEMP_CRIT?????????GENMASK(23, 16) > > > > +#define GET_TEMP_MAX(x)????????????????(((x) & DIMM_TEMP_MAX) >> 8) > > > > +#define GET_TEMP_CRIT(x)???????(((x) & DIMM_TEMP_CRIT) >> 16) > > > > + > > > > +struct peci_dimmtemp; > > > > + > > > > +struct dimm_info { > > > > +???????int chan_rank_max; > > > > +???????int dimm_idx_max; > > > > +???????u8 min_peci_revision; > > > > +???????int (*read_thresholds)(struct peci_dimmtemp *priv, int > > > > dimm_order, > > > > +????????????????????????????? int chan_rank, u32 *data); > > > > +}; > > > > + > > > > +struct peci_dimm_thresholds { > > > > +???????long temp_max; > > > > +???????long temp_crit; > > > > +???????struct peci_sensor_state state; > > > > +}; > > > > + > > > > +enum peci_dimm_threshold_type { > > > > +???????temp_max_type, > > > > +???????temp_crit_type, > > > > +}; > > > > + > > > > +struct peci_dimmtemp { > > > > +???????struct peci_device *peci_dev; > > > > +???????struct device *dev; > > > > +???????const char *name; > > > > +???????const struct dimm_info *gen_info; > > > > +???????struct delayed_work detect_work; > > > > +???????struct { > > > > +???????????????struct peci_sensor_data temp; > > > > +???????????????struct peci_dimm_thresholds thresholds; > > > > +???????} dimm[DIMM_NUMS_MAX]; > > > > +???????char **dimmtemp_label; > > > > +???????DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX); > > > > +}; > > > > + > > > > +static u8 __dimm_temp(u32 reg, int dimm_order) > > > > +{ > > > > +???????return (reg >> (dimm_order * 8)) & 0xff; > > > > +} > > > > + > > > > +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long > > > > *val) > > > > +{ > > > > +???????int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; > > > > +???????int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; > > > > +???????u32 data; > > > > +???????int ret; > > > > > > ?????????int ret = 0; > > > > > > > + > > > > +???????mutex_lock(&priv->dimm[dimm_no].temp.state.lock); > > > > +???????if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) > > > > +???????????????goto skip_update; > > > > + > > > > +???????ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, > > > > chan_rank, &data); > > > > +???????if (ret) { > > > > +???????????????mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); > > > > +???????????????return ret; > > > > +???????} > > > > > > ?????????if (ret) > > > ?????????????????goto unlock; > > > > > > > + > > > > +???????priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * > > > > MILLIDEGREE_PER_DEGREE; > > > > + > > > > +???????peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state); > > > > + > > > > +skip_update: > > > > +???????*val = priv->dimm[dimm_no].temp.value; > > > > > > unlock: > > > > +???????mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); > > > > +???????return 0; > > > > > > ?????????return ret; > > > > Ack. > > > > > > > > > +} > > > > + > > > > +static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) > > > > +{ > > > > +???????int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; > > > > +???????int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; > > > > +???????u32 data; > > > > +???????int ret; > > > > + > > > > +???????if (!peci_sensor_need_update(&priv- > > > > >dimm[dimm_no].thresholds.state)) > > > > +???????????????return 0; > > > > + > > > > +???????ret = priv->gen_info->read_thresholds(priv, dimm_order, > > > > chan_rank, > > > > &data); > > > > +???????if (ret == -ENODATA) /* Use default or previous value */ > > > > +???????????????return 0; > > > > +???????if (ret) > > > > +???????????????return ret; > > > > + > > > > +???????priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * > > > > MILLIDEGREE_PER_DEGREE; > > > > +???????priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * > > > > MILLIDEGREE_PER_DEGREE; > > > > + > > > > +???????peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state); > > > > + > > > > +???????return 0; > > > > +} > > > > + > > > > +static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum > > > > peci_dimm_threshold_type type, > > > > +????????????????????????????? int dimm_no, long *val) > > > > +{ > > > > +???????int ret; > > > > + > > > > +???????mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); > > > > +???????ret = update_thresholds(priv, dimm_no); > > > > +???????if (ret) > > > > +???????????????goto unlock; > > > > + > > > > +???????switch (type) { > > > > +???????case temp_max_type: > > > > +???????????????*val = priv->dimm[dimm_no].thresholds.temp_max; > > > > +???????????????break; > > > > +???????case temp_crit_type: > > > > +???????????????*val = priv->dimm[dimm_no].thresholds.temp_crit; > > > > +???????????????break; > > > > +???????default: > > > > +???????????????ret = -EOPNOTSUPP; > > > > +???????????????break; > > > > +???????} > > > > +unlock: > > > > +???????mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); > > > > + > > > > +???????return ret; > > > > +} > > > > + > > > > +static int dimmtemp_read_string(struct device *dev, > > > > +???????????????????????????????enum hwmon_sensor_types type, > > > > +???????????????????????????????u32 attr, int channel, const char **str) > > > > +{ > > > > +???????struct peci_dimmtemp *priv = dev_get_drvdata(dev); > > > > + > > > > +???????if (attr != hwmon_temp_label) > > > > +???????????????return -EOPNOTSUPP; > > > > + > > > > +???????*str = (const char *)priv->dimmtemp_label[channel]; > > > > + > > > > +???????return 0; > > > > +} > > > > + > > > > +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types > > > > type, > > > > +??????????????????????? u32 attr, int channel, long *val) > > > > +{ > > > > +???????struct peci_dimmtemp *priv = dev_get_drvdata(dev); > > > > + > > > > +???????switch (attr) { > > > > +???????case hwmon_temp_input: > > > > +???????????????return get_dimm_temp(priv, channel, val); > > > > +???????case hwmon_temp_max: > > > > +???????????????return get_dimm_thresholds(priv, temp_max_type, channel, > > > > val); > > > > +???????case hwmon_temp_crit: > > > > +???????????????return get_dimm_thresholds(priv, temp_crit_type, > > > > channel, > > > > val); > > > > +???????default: > > > > +???????????????break; > > > > +???????} > > > > + > > > > +???????return -EOPNOTSUPP; > > > > +} > > > > + > > > > +static umode_t dimmtemp_is_visible(const void *data, enum > > > > hwmon_sensor_types type, > > > > +????????????????????????????????? u32 attr, int channel) > > > > +{ > > > > +???????const struct peci_dimmtemp *priv = data; > > > > + > > > > +???????if (test_bit(channel, priv->dimm_mask)) > > > > +???????????????return 0444; > > > > + > > > > +???????return 0; > > > > +} > > > > + > > > > +static const struct hwmon_ops peci_dimmtemp_ops = { > > > > +???????.is_visible = dimmtemp_is_visible, > > > > +???????.read_string = dimmtemp_read_string, > > > > +???????.read = dimmtemp_read, > > > > +}; > > > > + > > > > +static int check_populated_dimms(struct peci_dimmtemp *priv) > > > > +{ > > > > +???????int chan_rank_max = priv->gen_info->chan_rank_max; > > > > +???????int dimm_idx_max = priv->gen_info->dimm_idx_max; > > > > +???????u32 chan_rank_empty = 0; > > > > +???????u64 dimm_mask = 0; > > > > +???????int chan_rank, dimm_idx, ret; > > > > +???????u32 pcs; > > > > + > > > > +???????BUILD_BUG_ON(CHAN_RANK_MAX > 32); > > > > +???????BUILD_BUG_ON(DIMM_NUMS_MAX > 64); > > > > > > I don't immediately see the value of those build bugs. What happens if > > > CHAN_RANK_MAX > 32 or DIMM_NUMS_MAX > 64 ? Where do those limits come > > > from ? > > > > Supported HW doesn't come near the limit for now - it's just an "artificial" > > limit imposed by variables we're using (u64 for dimm_mask and u32 for > > chan_rank_empty). > > > > Please use a value derived from the size of those variables for the check > to clarify and explain the constraints. Sure, I'll use BITS_PER_TYPE. Thanks -Iwona > > Thanks, > Guenter From rentao.bupt at gmail.com Fri Aug 6 08:28:14 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 5 Aug 2021 15:28:14 -0700 Subject: [PATCH v2 2/6] ARM: dts: aspeed: wedge400: Use common flash layout In-Reply-To: <20210805222818.8391-1-rentao.bupt@gmail.com> References: <20210805222818.8391-1-rentao.bupt@gmail.com> Message-ID: <20210805222818.8391-3-rentao.bupt@gmail.com> From: Tao Ren Simplify wedge400 flash layout by using the common layout defined in "facebook-bmc-flash-layout-128.dtsi". Signed-off-by: Tao Ren --- Changes in v2: - None. .../boot/dts/aspeed-bmc-facebook-wedge400.dts | 48 +------------------ 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-wedge400.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-wedge400.dts index 63a3dd548f30..a901c8be49b9 100644 --- a/arch/arm/boot/dts/aspeed-bmc-facebook-wedge400.dts +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-wedge400.dts @@ -91,53 +91,7 @@ * Both firmware flashes are 128MB on Wedge400 BMC. */ &fmc_flash0 { - partitions { - compatible = "fixed-partitions"; - #address-cells = <1>; - #size-cells = <1>; - - /* - * u-boot partition: 384KB. - */ - u-boot at 0 { - reg = <0x0 0x60000>; - label = "u-boot"; - }; - - /* - * u-boot environment variables: 128KB. - */ - u-boot-env at 60000 { - reg = <0x60000 0x20000>; - label = "env"; - }; - - /* - * FIT image: 123.5 MB. - */ - fit at 80000 { - reg = <0x80000 0x7b80000>; - label = "fit"; - }; - - /* - * "data0" partition (4MB) is reserved for persistent - * data store. - */ - data0 at 7c00000 { - reg = <0x7c00000 0x400000>; - label = "data0"; - }; - - /* - * "flash0" partition (covering the entire flash) is - * explicitly created to avoid breaking legacy applications. - */ - flash0 at 0 { - reg = <0x0 0x8000000>; - label = "flash0"; - }; - }; +#include "facebook-bmc-flash-layout-128.dtsi" }; &fmc_flash1 { -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 6 08:28:12 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 5 Aug 2021 15:28:12 -0700 Subject: [PATCH v2 0/6] ARM: dts: Add Facebook AST2600 BMCs Message-ID: <20210805222818.8391-1-rentao.bupt@gmail.com> From: Tao Ren The patch series introuces 3 new BMC platforms for the Facebook's next generation switch platforms: Cloudripper, Elbert and Fuji. Patch #1 adds dtsi file to describe the fixed flash layout of 128MB mtd device. The flash layout is used by all the 3 AST2600 BMC as well as Wedge400 (AST2500). Patch #2 simplies Wedge400 dts by including the flash layout dtsi. Patch #3 adds common dtsi which is included by all the new Facebook AST2600 Network BMC platforms. Patch #4 adds the device tree for Facebook Cloudripper (AST2600) BMC. Patch #5 adds the device tree for Facebook Elbert (AST2600) BMC. Patch #6 adds the device tree for Facebook Fuji (AST2600) BMC. Tao Ren (6): ARM: dts: Add Facebook BMC 128MB flash layout ARM: dts: aspeed: wedge400: Use common flash layout ARM: dts: aspeed: Common dtsi for Facebook AST2600 Network BMCs ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC ARM: dts: aspeed: Add Facebook Elbert (AST2600) BMC ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC arch/arm/boot/dts/Makefile | 3 + .../dts/aspeed-bmc-facebook-cloudripper.dts | 539 +++++++ .../boot/dts/aspeed-bmc-facebook-elbert.dts | 185 +++ .../arm/boot/dts/aspeed-bmc-facebook-fuji.dts | 1251 +++++++++++++++++ .../boot/dts/aspeed-bmc-facebook-wedge400.dts | 48 +- .../dts/ast2600-facebook-netbmc-common.dtsi | 169 +++ .../dts/facebook-bmc-flash-layout-128.dtsi | 60 + 7 files changed, 2208 insertions(+), 47 deletions(-) create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-elbert.dts create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-fuji.dts create mode 100644 arch/arm/boot/dts/ast2600-facebook-netbmc-common.dtsi create mode 100644 arch/arm/boot/dts/facebook-bmc-flash-layout-128.dtsi -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 6 08:28:13 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 5 Aug 2021 15:28:13 -0700 Subject: [PATCH v2 1/6] ARM: dts: Add Facebook BMC 128MB flash layout In-Reply-To: <20210805222818.8391-1-rentao.bupt@gmail.com> References: <20210805222818.8391-1-rentao.bupt@gmail.com> Message-ID: <20210805222818.8391-2-rentao.bupt@gmail.com> From: Tao Ren This is the layout used by Facebook BMC systems. It describes the fixed flash layout of a 128MB mtd device. Signed-off-by: Tao Ren --- Changes in v2: - None. .../dts/facebook-bmc-flash-layout-128.dtsi | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 arch/arm/boot/dts/facebook-bmc-flash-layout-128.dtsi diff --git a/arch/arm/boot/dts/facebook-bmc-flash-layout-128.dtsi b/arch/arm/boot/dts/facebook-bmc-flash-layout-128.dtsi new file mode 100644 index 000000000000..7f3652dea550 --- /dev/null +++ b/arch/arm/boot/dts/facebook-bmc-flash-layout-128.dtsi @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2020 Facebook Inc. + +partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* + * u-boot partition: 896KB. + */ + u-boot at 0 { + reg = <0x0 0xe0000>; + label = "u-boot"; + }; + + /* + * u-boot environment variables: 64KB. + */ + u-boot-env at e0000 { + reg = <0xe0000 0x10000>; + label = "env"; + }; + + /* + * image metadata partition (64KB), used by Facebook internal + * tools. + */ + image-meta at f0000 { + reg = <0xf0000 0x10000>; + label = "meta"; + }; + + /* + * FIT image: 119 MB. + */ + fit at 100000 { + reg = <0x100000 0x7700000>; + label = "fit"; + }; + + /* + * "data0" partition (8MB) is used by Facebook BMC platforms as + * persistent data store. + */ + data0 at 7800000 { + reg = <0x7800000 0x800000>; + label = "data0"; + }; + + /* + * Although the master partition can be created by enabling + * MTD_PARTITIONED_MASTER option, below "flash0" partition is + * explicitly created to avoid breaking legacy applications. + */ + flash0 at 0 { + reg = <0x0 0x8000000>; + label = "flash0"; + }; +}; -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 6 08:28:15 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 5 Aug 2021 15:28:15 -0700 Subject: [PATCH v2 3/6] ARM: dts: aspeed: Common dtsi for Facebook AST2600 Network BMCs In-Reply-To: <20210805222818.8391-1-rentao.bupt@gmail.com> References: <20210805222818.8391-1-rentao.bupt@gmail.com> Message-ID: <20210805222818.8391-4-rentao.bupt@gmail.com> From: Tao Ren This common descirption is included by all Facebook AST2600 Network BMC platforms to minimize duplicated device entries across Facebook Network BMC device trees. Signed-off-by: Tao Ren --- Changes in v2: - "mac3" is removed from the dtsi (moved to platform dts files). .../dts/ast2600-facebook-netbmc-common.dtsi | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 arch/arm/boot/dts/ast2600-facebook-netbmc-common.dtsi diff --git a/arch/arm/boot/dts/ast2600-facebook-netbmc-common.dtsi b/arch/arm/boot/dts/ast2600-facebook-netbmc-common.dtsi new file mode 100644 index 000000000000..051de5bec345 --- /dev/null +++ b/arch/arm/boot/dts/ast2600-facebook-netbmc-common.dtsi @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2020 Facebook Inc. + +#include "aspeed-g6.dtsi" +#include + +/ { + aliases { + mmc0 = &emmc; + spi1 = &spi1; + spi2 = &spi_gpio; + }; + + chosen { + bootargs = "console=ttyS0,9600n8 root=/dev/ram rw vmalloc=640M"; + }; + + memory at 80000000 { + device_type = "memory"; + reg = <0x80000000 0x80000000>; + }; + + /* + * GPIO-based SPI Master is required to access SPI TPM, because + * full-duplex SPI transactions are not supported by ASPEED SPI + * Controllers. + */ + spi_gpio: spi-gpio { + status = "okay"; + compatible = "spi-gpio"; + #address-cells = <1>; + #size-cells = <0>; + + gpio-sck = <&gpio0 ASPEED_GPIO(X, 3) GPIO_ACTIVE_HIGH>; + gpio-mosi = <&gpio0 ASPEED_GPIO(X, 4) GPIO_ACTIVE_HIGH>; + gpio-miso = <&gpio0 ASPEED_GPIO(X, 5) GPIO_ACTIVE_HIGH>; + + tpmdev at 0 { + compatible = "tcg,tpm_tis-spi"; + spi-max-frequency = <33000000>; + reg = <0>; + }; + }; +}; + +&fmc { + status = "okay"; + + flash at 0 { + status = "okay"; + m25p,fast-read; + label = "spi0.0"; + +#include "facebook-bmc-flash-layout-128.dtsi" + }; + + flash at 1 { + status = "okay"; + m25p,fast-read; + label = "spi0.1"; + + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + flash1 at 0 { + reg = <0x0 0x8000000>; + label = "flash1"; + }; + }; + }; +}; + +&spi1 { + status = "okay"; +}; + +&uart1 { + status = "okay"; +}; + +&uart2 { + status = "okay"; +}; + +&uart5 { + status = "okay"; +}; + +&wdt1 { + status = "okay"; +}; + +&i2c0 { + status = "okay"; +}; + +&i2c1 { + status = "okay"; +}; + +&i2c2 { + status = "okay"; +}; + +&i2c3 { + status = "okay"; +}; + +&i2c4 { + status = "okay"; +}; + +&i2c5 { + status = "okay"; +}; + +&i2c6 { + status = "okay"; +}; + +&i2c7 { + status = "okay"; +}; + +&i2c8 { + status = "okay"; +}; + +&i2c9 { + status = "okay"; +}; + +&i2c10 { + status = "okay"; +}; + +&i2c12 { + status = "okay"; +}; + +&i2c13 { + status = "okay"; +}; + +&i2c15 { + status = "okay"; +}; + +&vhub { + status = "okay"; +}; + +&emmc_controller { + status = "okay"; +}; + +&emmc { + status = "okay"; + + non-removable; + max-frequency = <25000000>; + bus-width = <4>; +}; + +&rtc { + status = "okay"; +}; -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 6 08:28:16 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 5 Aug 2021 15:28:16 -0700 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: <20210805222818.8391-1-rentao.bupt@gmail.com> References: <20210805222818.8391-1-rentao.bupt@gmail.com> Message-ID: <20210805222818.8391-5-rentao.bupt@gmail.com> From: Tao Ren Add initial version of device tree for Facebook Cloudripper (AST2600) BMC. Cloudripper is Facebook's next generation switch platform with an AST2600 BMC integrated for health monitoring purpose. Signed-off-by: Tao Ren --- Changes in v2: - added more notes about the new platform in patch description. - "spi1" was removed as the driver is not ready. - "mac3" properties were updated with pinctrl, and etc. arch/arm/boot/dts/Makefile | 1 + .../dts/aspeed-bmc-facebook-cloudripper.dts | 539 ++++++++++++++++++ 2 files changed, 540 insertions(+) create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 90aed2c2959a..74c760038f28 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1409,6 +1409,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \ aspeed-bmc-arm-stardragon4800-rep2.dtb \ aspeed-bmc-asrock-e3c246d4i.dtb \ aspeed-bmc-bytedance-g220a.dtb \ + aspeed-bmc-facebook-cloudripper.dtb \ aspeed-bmc-facebook-cmm.dtb \ aspeed-bmc-facebook-galaxy100.dtb \ aspeed-bmc-facebook-minipack.dtb \ diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts new file mode 100644 index 000000000000..01ec3ce0a29d --- /dev/null +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2020 Facebook Inc. + +/dts-v1/; + +#include +#include "ast2600-facebook-netbmc-common.dtsi" + +/ { + model = "Facebook Cloudripper BMC"; + compatible = "facebook,cloudripper-bmc", "aspeed,ast2600"; + + aliases { + /* + * PCA9548 (1-0070) provides 8 channels connecting to + * SMB (Switch Main Board). + */ + i2c16 = &imux16; + i2c17 = &imux17; + i2c18 = &imux18; + i2c19 = &imux19; + i2c20 = &imux20; + i2c21 = &imux21; + i2c22 = &imux22; + i2c23 = &imux23; + + /* + * PCA9548 (2-0070) provides 8 channels connecting to + * SCM (System Controller Module). + */ + i2c24 = &imux24; + i2c25 = &imux25; + i2c26 = &imux26; + i2c27 = &imux27; + i2c28 = &imux28; + i2c29 = &imux29; + i2c30 = &imux30; + i2c31 = &imux31; + + /* + * PCA9548 (3-0070) provides 8 channels connecting to + * SMB (Switch Main Board). + */ + i2c32 = &imux32; + i2c33 = &imux33; + i2c34 = &imux34; + i2c35 = &imux35; + i2c36 = &imux36; + i2c37 = &imux37; + i2c38 = &imux38; + i2c39 = &imux39; + + /* + * PCA9548 (8-0070) provides 8 channels connecting to + * PDB (Power Delivery Board). + */ + i2c40 = &imux40; + i2c41 = &imux41; + i2c42 = &imux42; + i2c43 = &imux43; + i2c44 = &imux44; + i2c45 = &imux45; + i2c46 = &imux46; + i2c47 = &imux47; + + /* + * PCA9548 (15-0076) provides 8 channels connecting to + * FCM (Fan Controller Module). + */ + i2c48 = &imux48; + i2c49 = &imux49; + i2c50 = &imux50; + i2c51 = &imux51; + i2c52 = &imux52; + i2c53 = &imux53; + i2c54 = &imux54; + i2c55 = &imux55; + }; + + spi_gpio: spi-gpio { + num-chipselects = <2>; + cs-gpios = <&gpio0 ASPEED_GPIO(X, 0) GPIO_ACTIVE_LOW>, + <&gpio0 ASPEED_GPIO(X, 1) GPIO_ACTIVE_HIGH>; + + eeprom at 1 { + compatible = "atmel,at93c46d"; + spi-max-frequency = <250000>; + data-size = <16>; + spi-cs-high; + reg = <1>; + }; + }; +}; + +&ehci1 { + status = "okay"; +}; + +&mdio1 { + status = "okay"; +}; + +&mdio3 { + status = "okay"; + + ethphy1: ethernet-phy at 13 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x0d>; + }; +}; + +&mac3 { + status = "okay"; + phy-mode = "rgmii"; + phy-handle = <ðphy1>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii4_default>; +}; + +&i2c0 { + multi-master; + bus-frequency = <1000000>; +}; + +&i2c1 { + /* + * PCA9548 (1-0070) provides 8 channels connecting to SMB (Switch + * Main Board). + */ + i2c-switch at 70 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + i2c-mux-idle-disconnect; + + imux16: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux17: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux18: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux19: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux20: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux21: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux22: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux23: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; +}; + +&i2c2 { + /* + * PCA9548 (2-0070) provides 8 channels connecting to SCM (System + * Controller Module). + */ + i2c-switch at 70 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + i2c-mux-idle-disconnect; + + imux24: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux25: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux26: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux27: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux28: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux29: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux30: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux31: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; +}; + +&i2c3 { + /* + * PCA9548 (3-0070) provides 8 channels connecting to SMB (Switch + * Main Board). + */ + i2c-switch at 70 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + i2c-mux-idle-disconnect; + + imux32: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux33: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux34: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux35: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux36: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux37: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux38: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux39: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; +}; + +&i2c6 { + lp5012 at 14 { + compatible = "ti,lp5012"; + reg = <0x14>; + #address-cells = <1>; + #size-cells = <0>; + + multi-led at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "sys"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + + multi-led at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "fan"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + + multi-led at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "psu"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + + multi-led at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "scm"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + }; +}; + +&i2c8 { + /* + * PCA9548 (8-0070) provides 8 channels connecting to PDB (Power + * Delivery Board). + */ + i2c-switch at 70 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + i2c-mux-idle-disconnect; + + imux40: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux41: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux42: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux43: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux44: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux45: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux46: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux47: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + + }; +}; + +&i2c15 { + /* + * PCA9548 (15-0076) provides 8 channels connecting to FCM (Fan + * Controller Module). + */ + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux48: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux49: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux50: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux51: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux52: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux53: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux54: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux55: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; +}; -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 6 08:28:17 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 5 Aug 2021 15:28:17 -0700 Subject: [PATCH v2 5/6] ARM: dts: aspeed: Add Facebook Elbert (AST2600) BMC In-Reply-To: <20210805222818.8391-1-rentao.bupt@gmail.com> References: <20210805222818.8391-1-rentao.bupt@gmail.com> Message-ID: <20210805222818.8391-6-rentao.bupt@gmail.com> From: Tao Ren Add initial version of device tree for Facebook Elbert (AST2600) BMC. Elbert is Facebook's next generation switch platform with an AST2600 BMC integrated for health monitoring purpose. Signed-off-by: Tao Ren --- Changes in v2: - added more notes about the new platform in patch description. - "mac3" was disabled (because it was removed from Facebook AST2600 NetBMC common dtsi). Will add "mac3" back when "fixed-link" is supported in the MAC driver. arch/arm/boot/dts/Makefile | 1 + .../boot/dts/aspeed-bmc-facebook-elbert.dts | 185 ++++++++++++++++++ 2 files changed, 186 insertions(+) create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-elbert.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 74c760038f28..7cbb982a7c8b 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1411,6 +1411,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \ aspeed-bmc-bytedance-g220a.dtb \ aspeed-bmc-facebook-cloudripper.dtb \ aspeed-bmc-facebook-cmm.dtb \ + aspeed-bmc-facebook-elbert.dtb \ aspeed-bmc-facebook-galaxy100.dtb \ aspeed-bmc-facebook-minipack.dtb \ aspeed-bmc-facebook-tiogapass.dtb \ diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-elbert.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-elbert.dts new file mode 100644 index 000000000000..27b43fe099f1 --- /dev/null +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-elbert.dts @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2020 Facebook Inc. + +/dts-v1/; + +#include "ast2600-facebook-netbmc-common.dtsi" + +/ { + model = "Facebook Elbert BMC"; + compatible = "facebook,elbert-bmc", "aspeed,ast2600"; + + aliases { + serial0 = &uart5; + serial1 = &uart1; + serial2 = &uart2; + serial3 = &uart3; + + /* + * 8 child channels of PCA9548 2-0075. + */ + i2c16 = &imux16; + i2c17 = &imux17; + i2c18 = &imux18; + i2c19 = &imux19; + i2c20 = &imux20; + i2c21 = &imux21; + i2c22 = &imux22; + i2c23 = &imux23; + + /* + * 8 child channels of PCA9548 5-0075. + */ + i2c24 = &imux24; + i2c25 = &imux25; + i2c26 = &imux26; + i2c27 = &imux27; + i2c28 = &imux28; + i2c29 = &imux29; + i2c30 = &imux30; + i2c31 = &imux31; + }; + + chosen { + stdout-path = &uart5; + }; + + spi_gpio: spi-gpio { + num-chipselects = <1>; + cs-gpios = <&gpio0 ASPEED_GPIO(X, 0) GPIO_ACTIVE_LOW>; + }; +}; + +&lpc_ctrl { + status = "okay"; +}; + +&kcs2 { + status = "okay"; + aspeed,lpc-io-reg = <0xca8>; +}; + +&kcs3 { + status = "okay"; + aspeed,lpc-io-reg = <0xca2>; +}; + +&i2c2 { + i2c-switch at 75 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x75>; + i2c-mux-idle-disconnect; + + imux16: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux17: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux18: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux19: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux20: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux21: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux22: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux23: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; +}; + +&i2c5 { + i2c-switch at 75 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x75>; + i2c-mux-idle-disconnect; + + imux24: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux25: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux26: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux27: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux28: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux29: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux30: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux31: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; +}; + +&i2c11 { + status = "okay"; +}; -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 6 08:28:18 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 5 Aug 2021 15:28:18 -0700 Subject: [PATCH v2 6/6] ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC In-Reply-To: <20210805222818.8391-1-rentao.bupt@gmail.com> References: <20210805222818.8391-1-rentao.bupt@gmail.com> Message-ID: <20210805222818.8391-7-rentao.bupt@gmail.com> From: Tao Ren Add initial version of device tree for Facebook Fuji (AST2600) BMC. Fuji is Facebook's next generation switch platform with an AST2600 BMC integrated for health monitoring purpose. Signed-off-by: Tao Ren --- Changes in v2: - added more notes about the new platform in patch description. - "spi1" was removed as the driver is not ready. - "mac3" properties were updated with pinctrl, and etc. arch/arm/boot/dts/Makefile | 1 + .../arm/boot/dts/aspeed-bmc-facebook-fuji.dts | 1251 +++++++++++++++++ 2 files changed, 1252 insertions(+) create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-fuji.dts diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile index 7cbb982a7c8b..9765e2ff5bfe 100644 --- a/arch/arm/boot/dts/Makefile +++ b/arch/arm/boot/dts/Makefile @@ -1412,6 +1412,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \ aspeed-bmc-facebook-cloudripper.dtb \ aspeed-bmc-facebook-cmm.dtb \ aspeed-bmc-facebook-elbert.dtb \ + aspeed-bmc-facebook-fuji.dtb \ aspeed-bmc-facebook-galaxy100.dtb \ aspeed-bmc-facebook-minipack.dtb \ aspeed-bmc-facebook-tiogapass.dtb \ diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-fuji.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-fuji.dts new file mode 100644 index 000000000000..af58a73bbc49 --- /dev/null +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-fuji.dts @@ -0,0 +1,1251 @@ +// SPDX-License-Identifier: GPL-2.0+ +// Copyright (c) 2020 Facebook Inc. + +/dts-v1/; + +#include +#include "ast2600-facebook-netbmc-common.dtsi" + +/ { + model = "Facebook Fuji BMC"; + compatible = "facebook,fuji-bmc", "aspeed,ast2600"; + + aliases { + /* + * PCA9548 (2-0070) provides 8 channels connecting to + * SCM (System Controller Module). + */ + i2c16 = &imux16; + i2c17 = &imux17; + i2c18 = &imux18; + i2c19 = &imux19; + i2c20 = &imux20; + i2c21 = &imux21; + i2c22 = &imux22; + i2c23 = &imux23; + + /* + * PCA9548 (8-0070) provides 8 channels connecting to + * SMB (Switch Main Board). + */ + i2c24 = &imux24; + i2c25 = &imux25; + i2c26 = &imux26; + i2c27 = &imux27; + i2c28 = &imux28; + i2c29 = &imux29; + i2c30 = &imux30; + i2c31 = &imux31; + + /* + * PCA9548 (11-0077) provides 8 channels connecting to + * SMB (Switch Main Board). + */ + i2c40 = &imux40; + i2c41 = &imux41; + i2c42 = &imux42; + i2c43 = &imux43; + i2c44 = &imux44; + i2c45 = &imux45; + i2c46 = &imux46; + i2c47 = &imux47; + + /* + * PCA9548 (24-0071) provides 8 channels connecting to + * PDB-Left. + */ + i2c48 = &imux48; + i2c49 = &imux49; + i2c50 = &imux50; + i2c51 = &imux51; + i2c52 = &imux52; + i2c53 = &imux53; + i2c54 = &imux54; + i2c55 = &imux55; + + /* + * PCA9548 (25-0072) provides 8 channels connecting to + * PDB-Right. + */ + i2c56 = &imux56; + i2c57 = &imux57; + i2c58 = &imux58; + i2c59 = &imux59; + i2c60 = &imux60; + i2c61 = &imux61; + i2c62 = &imux62; + i2c63 = &imux63; + + /* + * PCA9548 (26-0076) provides 8 channels connecting to + * FCM1. + */ + i2c64 = &imux64; + i2c65 = &imux65; + i2c66 = &imux66; + i2c67 = &imux67; + i2c68 = &imux68; + i2c69 = &imux69; + i2c70 = &imux70; + i2c71 = &imux71; + + /* + * PCA9548 (27-0076) provides 8 channels connecting to + * FCM2. + */ + i2c72 = &imux72; + i2c73 = &imux73; + i2c74 = &imux74; + i2c75 = &imux75; + i2c76 = &imux76; + i2c77 = &imux77; + i2c78 = &imux78; + i2c79 = &imux79; + + /* + * PCA9548 (40-0076) provides 8 channels connecting to + * PIM1. + */ + i2c80 = &imux80; + i2c81 = &imux81; + i2c82 = &imux82; + i2c83 = &imux83; + i2c84 = &imux84; + i2c85 = &imux85; + i2c86 = &imux86; + i2c87 = &imux87; + + /* + * PCA9548 (41-0076) provides 8 channels connecting to + * PIM2. + */ + i2c88 = &imux88; + i2c89 = &imux89; + i2c90 = &imux90; + i2c91 = &imux91; + i2c92 = &imux92; + i2c93 = &imux93; + i2c94 = &imux94; + i2c95 = &imux95; + + /* + * PCA9548 (42-0076) provides 8 channels connecting to + * PIM3. + */ + i2c96 = &imux96; + i2c97 = &imux97; + i2c98 = &imux98; + i2c99 = &imux99; + i2c100 = &imux100; + i2c101 = &imux101; + i2c102 = &imux102; + i2c103 = &imux103; + + /* + * PCA9548 (43-0076) provides 8 channels connecting to + * PIM4. + */ + i2c104 = &imux104; + i2c105 = &imux105; + i2c106 = &imux106; + i2c107 = &imux107; + i2c108 = &imux108; + i2c109 = &imux109; + i2c110 = &imux110; + i2c111 = &imux111; + + /* + * PCA9548 (44-0076) provides 8 channels connecting to + * PIM5. + */ + i2c112 = &imux112; + i2c113 = &imux113; + i2c114 = &imux114; + i2c115 = &imux115; + i2c116 = &imux116; + i2c117 = &imux117; + i2c118 = &imux118; + i2c119 = &imux119; + + /* + * PCA9548 (45-0076) provides 8 channels connecting to + * PIM6. + */ + i2c120 = &imux120; + i2c121 = &imux121; + i2c122 = &imux122; + i2c123 = &imux123; + i2c124 = &imux124; + i2c125 = &imux125; + i2c126 = &imux126; + i2c127 = &imux127; + + /* + * PCA9548 (46-0076) provides 8 channels connecting to + * PIM7. + */ + i2c128 = &imux128; + i2c129 = &imux129; + i2c130 = &imux130; + i2c131 = &imux131; + i2c132 = &imux132; + i2c133 = &imux133; + i2c134 = &imux134; + i2c135 = &imux135; + + /* + * PCA9548 (47-0076) provides 8 channels connecting to + * PIM8. + */ + i2c136 = &imux136; + i2c137 = &imux137; + i2c138 = &imux138; + i2c139 = &imux139; + i2c140 = &imux140; + i2c141 = &imux141; + i2c142 = &imux142; + i2c143 = &imux143; + }; + + spi_gpio: spi-gpio { + num-chipselects = <3>; + cs-gpios = <&gpio0 ASPEED_GPIO(X, 0) GPIO_ACTIVE_LOW>, + <0>, /* device reg=<1> does not exist */ + <&gpio0 ASPEED_GPIO(X, 2) GPIO_ACTIVE_HIGH>; + + eeprom at 2 { + compatible = "atmel,at93c46d"; + spi-max-frequency = <250000>; + data-size = <16>; + spi-cs-high; + reg = <2>; + }; + }; +}; + +&i2c0 { + multi-master; + bus-frequency = <1000000>; +}; + +&i2c2 { + /* + * PCA9548 (2-0070) provides 8 channels connecting to SCM (System + * Controller Module). + */ + i2c-switch at 70 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + i2c-mux-idle-disconnect; + + imux16: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + adm1278 at 10 { + compatible = "adi,adm1278"; + reg = <0x10>; + #address-cells = <1>; + #size-cells = <0>; + shunt-resistor-micro-ohms = <1500>; + }; + }; + + imux17: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux18: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux19: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux20: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux21: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux22: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux23: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; +}; + +&i2c8 { + /* + * PCA9548 (8-0070) provides 8 channels connecting to SMB (Switch + * Main Board). + */ + i2c-switch at 70 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x70>; + i2c-mux-idle-disconnect; + + imux24: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + i2c-switch at 71 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x71>; + i2c-mux-idle-disconnect; + + imux48: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux49: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux50: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + lp5012 at 14 { + compatible = "ti,lp5012"; + reg = <0x14>; + #address-cells = <1>; + #size-cells = <0>; + + multi-led at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "sys"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + + multi-led at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "fan"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + + multi-led at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "psu"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + + multi-led at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + color = ; + function = LED_FUNCTION_ACTIVITY; + label = "smb"; + + led at 0 { + reg = <0>; + color = ; + }; + + led at 1 { + reg = <1>; + color = ; + }; + + led at 2 { + reg = <2>; + color = ; + }; + }; + }; + }; + + imux51: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux52: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux53: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux54: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux55: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux25: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + i2c-switch at 72 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x72>; + i2c-mux-idle-disconnect; + + imux56: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux57: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux58: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux59: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux60: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux61: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux62: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux63: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux26: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux64: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux65: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux66: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux67: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + adm1278 at 10 { + compatible = "adi,adm1278"; + reg = <0x10>; + #address-cells = <1>; + #size-cells = <0>; + shunt-resistor-micro-ohms = <250>; + }; + }; + + imux68: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux69: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux70: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux71: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux27: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux72: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux73: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux74: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux75: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + adm1278 at 10 { + compatible = "adi,adm1278"; + reg = <0x10>; + #address-cells = <1>; + #size-cells = <0>; + shunt-resistor-micro-ohms = <250>; + }; + }; + + imux76: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux77: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux78: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux79: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux28: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux29: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux30: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux31: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + + }; +}; + +&i2c11 { + status = "okay"; + + /* + * PCA9548 (11-0077) provides 8 channels connecting to SMB (Switch + * Main Board). + */ + i2c-switch at 77 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x77>; + i2c-mux-idle-disconnect; + + imux40: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux80: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux81: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux82: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux83: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux84: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux85: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux86: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux87: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux41: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux88: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux89: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux90: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux91: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux92: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux93: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux94: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux95: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux42: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux96: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux97: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux98: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux99: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux100: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux101: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux102: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux103: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux43: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux104: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux105: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux106: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux107: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux108: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux109: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux110: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux111: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux44: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux112: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux113: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux114: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux115: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux116: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux117: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux118: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux119: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux45: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux120: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux121: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux122: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux123: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux124: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux125: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux126: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux127: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux46: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux128: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux129: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux130: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux131: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux132: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux133: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux134: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux135: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + imux47: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + + i2c-switch at 76 { + compatible = "nxp,pca9548"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x76>; + i2c-mux-idle-disconnect; + + imux136: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + }; + + imux137: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + }; + + imux138: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + imux139: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + + imux140: i2c at 4 { + #address-cells = <1>; + #size-cells = <0>; + reg = <4>; + }; + + imux141: i2c at 5 { + #address-cells = <1>; + #size-cells = <0>; + reg = <5>; + }; + + imux142: i2c at 6 { + #address-cells = <1>; + #size-cells = <0>; + reg = <6>; + }; + + imux143: i2c at 7 { + #address-cells = <1>; + #size-cells = <0>; + reg = <7>; + }; + }; + + }; + + }; +}; + +&ehci1 { + status = "okay"; +}; + +&mdio1 { + status = "okay"; + + ethphy3: ethernet-phy at 13 { + compatible = "ethernet-phy-ieee802.3-c22"; + reg = <0x0d>; + }; +}; + +&mac3 { + status = "okay"; + phy-mode = "rgmii"; + phy-handle = <ðphy3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_rgmii4_default>; +}; -- 2.17.1 From iwona.winiarska at intel.com Wed Aug 4 20:43:31 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Wed, 4 Aug 2021 10:43:31 +0000 Subject: [PATCH v2 12/15] hwmon: peci: Add cputemp driver In-Reply-To: References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-13-iwona.winiarska@intel.com> Message-ID: <7271a56f22ef33cf178683456dbb7c216951018b.camel@intel.com> On Tue, 2021-08-03 at 08:24 -0700, Guenter Roeck wrote: > On 8/3/21 4:31 AM, Iwona Winiarska wrote: > > Add peci-cputemp driver for Digital Thermal Sensor (DTS) thermal > > readings of the processor package and processor cores that are > > accessible via the PECI interface. > > > > The main use case for the driver (and PECI interface) is out-of-band > > management, where we're able to obtain the DTS readings from an external > > entity connected with PECI, e.g. BMC on server platforms. > > > > Co-developed-by: Jae Hyun Yoo > > Signed-off-by: Jae Hyun Yoo > > Signed-off-by: Iwona Winiarska > > Reviewed-by: Pierre-Louis Bossart > > --- > > ? MAINTAINERS????????????????? |?? 7 + > > ? drivers/hwmon/Kconfig??????? |?? 2 + > > ? drivers/hwmon/Makefile?????? |?? 1 + > > ? drivers/hwmon/peci/Kconfig?? |? 18 ++ > > ? drivers/hwmon/peci/Makefile? |?? 5 + > > ? drivers/hwmon/peci/common.h? |? 58 ++++ > > ? drivers/hwmon/peci/cputemp.c | 591 +++++++++++++++++++++++++++++++++++ > > ? 7 files changed, 682 insertions(+) > > ? create mode 100644 drivers/hwmon/peci/Kconfig > > ? create mode 100644 drivers/hwmon/peci/Makefile > > ? create mode 100644 drivers/hwmon/peci/common.h > > ? create mode 100644 drivers/hwmon/peci/cputemp.c > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 3f5d48e1d143..e36b5c0824e3 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -14512,6 +14512,13 @@ L:?????platform-driver-x86 at vger.kernel.org > > ? S:????Maintained > > ? F:????drivers/platform/x86/peaq-wmi.c > > ? > > +PECI HARDWARE MONITORING DRIVERS > > +M:?????Iwona Winiarska > > +R:?????Jae Hyun Yoo > > +L:?????linux-hwmon at vger.kernel.org > > +S:?????Supported > > +F:?????drivers/hwmon/peci/ > > + > > ? PECI SUBSYSTEM > > ? M:????Iwona Winiarska > > ? R:????Jae Hyun Yoo > > diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig > > index e3675377bc5d..61c0e3404415 100644 > > --- a/drivers/hwmon/Kconfig > > +++ b/drivers/hwmon/Kconfig > > @@ -1507,6 +1507,8 @@ config SENSORS_PCF8591 > > ????????? These devices are hard to detect and rarely found on mainstream > > ????????? hardware. If unsure, say N. > > ? > > +source "drivers/hwmon/peci/Kconfig" > > + > > ? source "drivers/hwmon/pmbus/Kconfig" > > ? > > ? config SENSORS_PWM_FAN > > diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile > > index d712c61c1f5e..f52331f212ed 100644 > > --- a/drivers/hwmon/Makefile > > +++ b/drivers/hwmon/Makefile > > @@ -202,6 +202,7 @@ obj-$(CONFIG_SENSORS_WM8350)????????+= wm8350-hwmon.o > > ? obj-$(CONFIG_SENSORS_XGENE)???+= xgene-hwmon.o > > ? > > ? obj-$(CONFIG_SENSORS_OCC)?????+= occ/ > > +obj-$(CONFIG_SENSORS_PECI)?????+= peci/ > > ? obj-$(CONFIG_PMBUS)???????????+= pmbus/ > > ? > > ? ccflags-$(CONFIG_HWMON_DEBUG_CHIP) := -DDEBUG > > diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig > > new file mode 100644 > > index 000000000000..e10eed68d70a > > --- /dev/null > > +++ b/drivers/hwmon/peci/Kconfig > > @@ -0,0 +1,18 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +config SENSORS_PECI_CPUTEMP > > +???????tristate "PECI CPU temperature monitoring client" > > +???????depends on PECI > > +???????select SENSORS_PECI > > +???????select PECI_CPU > > +???????help > > +???????? If you say yes here you get support for the generic Intel PECI > > +???????? cputemp driver which provides Digital Thermal Sensor (DTS) thermal > > +???????? readings of the CPU package and CPU cores that are accessible via > > +???????? the processor PECI interface. > > + > > +???????? This driver can also be built as a module. If so, the module > > +???????? will be called peci-cputemp. > > + > > +config SENSORS_PECI > > +???????tristate > > diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile > > new file mode 100644 > > index 000000000000..e8a0ada5ab1f > > --- /dev/null > > +++ b/drivers/hwmon/peci/Makefile > > @@ -0,0 +1,5 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +peci-cputemp-y := cputemp.o > > + > > +obj-$(CONFIG_SENSORS_PECI_CPUTEMP)?????+= peci-cputemp.o > > diff --git a/drivers/hwmon/peci/common.h b/drivers/hwmon/peci/common.h > > new file mode 100644 > > index 000000000000..734506b0eca2 > > --- /dev/null > > +++ b/drivers/hwmon/peci/common.h > > @@ -0,0 +1,58 @@ > > +/* SPDX-License-Identifier: GPL-2.0-only */ > > +/* Copyright (c) 2021 Intel Corporation */ > > + > > +#include > > +#include > > + > > +#ifndef __PECI_HWMON_COMMON_H > > +#define __PECI_HWMON_COMMON_H > > + > > +#define PECI_HWMON_UPDATE_INTERVAL?????HZ > > + > > +/** > > + * struct peci_sensor_state - PECI state information > > + * @valid: flag to indicate the sensor value is valid > > + * @last_updated: time of the last update in jiffies > > + * @lock: mutex to protect sensor access > > + */ > > +struct peci_sensor_state { > > +???????bool valid; > > +???????unsigned long last_updated; > > +???????struct mutex lock; /* protect sensor access */ > > +}; > > + > > +/** > > + * struct peci_sensor_data - PECI sensor information > > + * @value: sensor value in milli units > > + * @state: sensor update state > > + */ > > + > > +struct peci_sensor_data { > > +???????s32 value; > > +???????struct peci_sensor_state state; > > +}; > > + > > +/** > > + * peci_sensor_need_update() - check whether sensor update is needed or not > > + * @sensor: pointer to sensor data struct > > + * > > + * Return: true if update is needed, false if not. > > + */ > > + > > +static inline bool peci_sensor_need_update(struct peci_sensor_state *state) > > +{ > > +???????return !state->valid || > > +????????????? time_after(jiffies, state->last_updated + > > PECI_HWMON_UPDATE_INTERVAL); > > +} > > + > > +/** > > + * peci_sensor_mark_updated() - mark the sensor is updated > > + * @sensor: pointer to sensor data struct > > + */ > > +static inline void peci_sensor_mark_updated(struct peci_sensor_state > > *state) > > +{ > > +???????state->valid = true; > > +???????state->last_updated = jiffies; > > +} > > + > > +#endif /* __PECI_HWMON_COMMON_H */ > > diff --git a/drivers/hwmon/peci/cputemp.c b/drivers/hwmon/peci/cputemp.c > > new file mode 100644 > > index 000000000000..9c6858a9fb6d > > --- /dev/null > > +++ b/drivers/hwmon/peci/cputemp.c > > @@ -0,0 +1,591 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +// Copyright (c) 2018-2021 Intel Corporation > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include "common.h" > > + > > +#define CORE_NUMS_MAX??????????64 > > + > > +#define BASE_CHANNEL_NUMS??????5 > > +#define CPUTEMP_CHANNEL_NUMS???(BASE_CHANNEL_NUMS + CORE_NUMS_MAX) > > + > > +#define TEMP_TARGET_FAN_TEMP_MASK??????GENMASK(15, 8) > > +#define TEMP_TARGET_REF_TEMP_MASK??????GENMASK(23, 16) > > +#define TEMP_TARGET_TJ_OFFSET_MASK?????GENMASK(29, 24) > > + > > +#define DTS_MARGIN_MASK????????????????GENMASK(15, 0) > > +#define PCS_MODULE_TEMP_MASK???GENMASK(15, 0) > > + > > +#define DTS_FIXED_POINT_FRACTION???????64 > > + > > +struct resolved_cores_reg { > > +???????u8 bus; > > +???????u8 dev; > > +???????u8 func; > > +???????u8 offset; > > +}; > > + > > +struct cpu_info { > > +???????struct resolved_cores_reg *reg; > > +???????u8 min_peci_revision; > > +}; > > + > > +struct peci_temp_target { > > +???????s32 tcontrol; > > +???????s32 tthrottle; > > +???????s32 tjmax; > > +???????struct peci_sensor_state state; > > +}; > > + > > +enum peci_temp_target_type { > > +???????tcontrol_type, > > +???????tthrottle_type, > > +???????tjmax_type, > > +???????crit_hyst_type, > > +}; > > + > > +struct peci_cputemp { > > +???????struct peci_device *peci_dev; > > +???????struct device *dev; > > +???????const char *name; > > +???????const struct cpu_info *gen_info; > > +???????struct { > > +???????????????struct peci_temp_target target; > > +???????????????struct peci_sensor_data die; > > +???????????????struct peci_sensor_data dts; > > +???????????????struct peci_sensor_data core[CORE_NUMS_MAX]; > > +???????} temp; > > +???????const char **coretemp_label; > > +???????DECLARE_BITMAP(core_mask, CORE_NUMS_MAX); > > +}; > > + > > +enum cputemp_channels { > > +???????channel_die, > > +???????channel_dts, > > +???????channel_tcontrol, > > +???????channel_tthrottle, > > +???????channel_tjmax, > > +???????channel_core, > > +}; > > + > > +static const char * const cputemp_label[BASE_CHANNEL_NUMS] = { > > +???????"Die", > > +???????"DTS", > > +???????"Tcontrol", > > +???????"Tthrottle", > > +???????"Tjmax", > > +}; > > + > > +static int update_temp_target(struct peci_cputemp *priv) > > +{ > > +???????s32 tthrottle_offset, tcontrol_margin; > > +???????u32 pcs; > > +???????int ret; > > + > > +???????if (!peci_sensor_need_update(&priv->temp.target.state)) > > +???????????????return 0; > > + > > +???????ret = peci_pcs_read(priv->peci_dev, PECI_PCS_TEMP_TARGET, 0, &pcs); > > +???????if (ret) > > +???????????????return ret; > > + > > +???????priv->temp.target.tjmax = > > +???????????????FIELD_GET(TEMP_TARGET_REF_TEMP_MASK, pcs) * > > MILLIDEGREE_PER_DEGREE; > > + > > +???????tcontrol_margin = FIELD_GET(TEMP_TARGET_FAN_TEMP_MASK, pcs); > > +???????tcontrol_margin = sign_extend32(tcontrol_margin, 7) * > > MILLIDEGREE_PER_DEGREE; > > +???????priv->temp.target.tcontrol = priv->temp.target.tjmax - > > tcontrol_margin; > > + > > +???????tthrottle_offset = FIELD_GET(TEMP_TARGET_TJ_OFFSET_MASK, pcs) * > > MILLIDEGREE_PER_DEGREE; > > +???????priv->temp.target.tthrottle = priv->temp.target.tjmax - > > tthrottle_offset; > > + > > +???????peci_sensor_mark_updated(&priv->temp.target.state); > > + > > +???????return 0; > > +} > > + > > +static int get_temp_target(struct peci_cputemp *priv, enum > > peci_temp_target_type type, long *val) > > +{ > > +???????int ret; > > + > > +???????mutex_lock(&priv->temp.target.state.lock); > > + > > +???????ret = update_temp_target(priv); > > +???????if (ret) > > +???????????????goto unlock; > > + > > +???????switch (type) { > > +???????case tcontrol_type: > > +???????????????*val = priv->temp.target.tcontrol; > > +???????????????break; > > +???????case tthrottle_type: > > +???????????????*val = priv->temp.target.tthrottle; > > +???????????????break; > > +???????case tjmax_type: > > +???????????????*val = priv->temp.target.tjmax; > > +???????????????break; > > +???????case crit_hyst_type: > > +???????????????*val = priv->temp.target.tjmax - priv->temp.target.tcontrol; > > +???????????????break; > > +???????default: > > +???????????????ret = -EOPNOTSUPP; > > +???????????????break; > > +???????} > > +unlock: > > +???????mutex_unlock(&priv->temp.target.state.lock); > > + > > +???????return ret; > > +} > > + > > +/* > > + * Processors return a value of DTS reading in S10.6 fixed point format > > + * (16 bits: 10-bit signed magnitude, 6-bit fraction). > > + * Error codes: > > + *?? 0x8000: General sensor error > > + *?? 0x8001: Reserved > > + *?? 0x8002: Underflow on reading value > > + *?? 0x8003-0x81ff: Reserved > > + */ > > +static bool dts_valid(s32 val) > > +{ > > +???????return val < 0x8000 || val > 0x81ff; > > +} > > + > > +static s32 dts_to_millidegree(s32 val) > > +{ > > +???????return sign_extend32(val, 15) * MILLIDEGREE_PER_DEGREE / > > DTS_FIXED_POINT_FRACTION; > > +} > > + > > +static int get_die_temp(struct peci_cputemp *priv, long *val) > > +{ > > +???????long tjmax; > > +???????s16 temp; > > +???????int ret; > > + > > +???????mutex_lock(&priv->temp.die.state.lock); > > +???????if (!peci_sensor_need_update(&priv->temp.die.state)) > > +???????????????goto skip_update; > > + > > +???????ret = peci_temp_read(priv->peci_dev, &temp); > > +???????if (ret) > > +???????????????goto err_unlock; > > + > > +???????if (!dts_valid(temp)) { > > +???????????????ret = -EIO; > > +???????????????goto err_unlock; > > +???????} > > + > > +???????ret = get_temp_target(priv, tjmax_type, &tjmax); > > +???????if (ret) > > +???????????????goto err_unlock; > > + > > +???????priv->temp.die.value = (s32)tjmax + dts_to_millidegree(temp); > > + > > +???????peci_sensor_mark_updated(&priv->temp.die.state); > > + > > +skip_update: > > +???????*val = priv->temp.die.value; > > +???????mutex_unlock(&priv->temp.die.state.lock); > > + > > +???????return 0; > > + > > +err_unlock: > > +???????mutex_unlock(&priv->temp.die.state.lock); > > +???????return ret; > > +} > > + > > +static int get_dts(struct peci_cputemp *priv, long *val) > > +{ > > +???????s32 dts_margin; > > +???????long tcontrol; > > +???????u32 pcs; > > +???????int ret; > > + > > +???????mutex_lock(&priv->temp.dts.state.lock); > > +???????if (!peci_sensor_need_update(&priv->temp.dts.state)) > > +???????????????goto skip_update; > > + > > +???????ret = peci_pcs_read(priv->peci_dev, PECI_PCS_THERMAL_MARGIN, 0, > > &pcs); > > +???????if (ret) > > +???????????????goto err_unlock; > > + > > +???????dts_margin = FIELD_GET(DTS_MARGIN_MASK, pcs); > > +???????if (!dts_valid(dts_margin)) { > > +???????????????ret = -EIO; > > +???????????????goto err_unlock; > > +???????} > > + > > +???????ret = get_temp_target(priv, tcontrol_type, &tcontrol); > > +???????if (ret) > > +???????????????goto err_unlock; > > + > > +???????/* Note that the tcontrol should be available before calling it */ > > +???????priv->temp.dts.value = (s32)tcontrol - > > dts_to_millidegree(dts_margin); > > + > > +???????peci_sensor_mark_updated(&priv->temp.dts.state); > > + > > +skip_update: > > +???????*val = priv->temp.dts.value; > > +???????mutex_unlock(&priv->temp.dts.state.lock); > > + > > +???????return 0; > > + > > +err_unlock: > > +???????mutex_unlock(&priv->temp.dts.state.lock); > > +???????return ret; > > Simplify (see below) > > > +} > > + > > +static int get_core_temp(struct peci_cputemp *priv, int core_index, long > > *val) > > +{ > > +???????s32 core_dts_margin; > > +???????long tjmax; > > +???????u32 pcs; > > +???????int ret; > > ????????int ret = 0; > > to handle simplification below. > > > + > > +???????mutex_lock(&priv->temp.core[core_index].state.lock); > > +???????if (!peci_sensor_need_update(&priv->temp.core[core_index].state)) > > +???????????????goto skip_update; > > + > > +???????ret = peci_pcs_read(priv->peci_dev, PECI_PCS_MODULE_TEMP, > > core_index, &pcs); > > +???????if (ret) > > +???????????????goto err_unlock; > > + > > +???????core_dts_margin = FIELD_GET(PCS_MODULE_TEMP_MASK, pcs); > > +???????if (!dts_valid(core_dts_margin)) { > > +???????????????ret = -EIO; > > +???????????????goto err_unlock; > > +???????} > > + > > +???????ret = get_temp_target(priv, tjmax_type, &tjmax); > > +???????if (ret) > > +???????????????goto err_unlock; > > + > > +???????/* Note that the tjmax should be available before calling it */ > > +???????priv->temp.core[core_index].value = (s32)tjmax + > > dts_to_millidegree(core_dts_margin); > > + > > +???????peci_sensor_mark_updated(&priv->temp.core[core_index].state); > > + > > +skip_update: > > +???????*val = priv->temp.core[core_index].value; > > +???????mutex_unlock(&priv->temp.core[core_index].state.lock); > > + > > +???????return 0; > > + > > +err_unlock: > > +???????mutex_unlock(&priv->temp.core[core_index].state.lock); > > +???????return ret; > > Simplify: > > skip_update: > ????????*val = priv->temp.core[core_index].value; > err_unlock: > ????????mutex_unlock(&priv->temp.core[core_index].state.lock); > ????????return ret; Sure, I'll use the same pattern in other places as well. Thank you -Iwona > > > +} > > + > > +static int cputemp_read_string(struct device *dev, enum hwmon_sensor_types > > type, > > +????????????????????????????? u32 attr, int channel, const char **str) > > +{ > > +???????struct peci_cputemp *priv = dev_get_drvdata(dev); > > + > > +???????if (attr != hwmon_temp_label) > > +???????????????return -EOPNOTSUPP; > > + > > +???????*str = channel < channel_core ? > > +???????????????cputemp_label[channel] : priv->coretemp_label[channel - > > channel_core]; > > + > > +???????return 0; > > +} > > + > > +static int cputemp_read(struct device *dev, enum hwmon_sensor_types type, > > +???????????????????????u32 attr, int channel, long *val) > > +{ > > +???????struct peci_cputemp *priv = dev_get_drvdata(dev); > > + > > +???????switch (attr) { > > +???????case hwmon_temp_input: > > +???????????????switch (channel) { > > +???????????????case channel_die: > > +???????????????????????return get_die_temp(priv, val); > > +???????????????case channel_dts: > > +???????????????????????return get_dts(priv, val); > > +???????????????case channel_tcontrol: > > +???????????????????????return get_temp_target(priv, tcontrol_type, val); > > +???????????????case channel_tthrottle: > > +???????????????????????return get_temp_target(priv, tthrottle_type, val); > > +???????????????case channel_tjmax: > > +???????????????????????return get_temp_target(priv, tjmax_type, val); > > +???????????????default: > > +???????????????????????return get_core_temp(priv, channel - channel_core, > > val); > > +???????????????} > > +???????????????break; > > +???????case hwmon_temp_max: > > +???????????????return get_temp_target(priv, tcontrol_type, val); > > +???????case hwmon_temp_crit: > > +???????????????return get_temp_target(priv, tjmax_type, val); > > +???????case hwmon_temp_crit_hyst: > > +???????????????return get_temp_target(priv, crit_hyst_type, val); > > +???????default: > > +???????????????return -EOPNOTSUPP; > > +???????} > > + > > +???????return 0; > > +} > > + > > +static umode_t cputemp_is_visible(const void *data, enum hwmon_sensor_types > > type, > > +???????????????????????????????? u32 attr, int channel) > > +{ > > +???????const struct peci_cputemp *priv = data; > > + > > +???????if (channel > CPUTEMP_CHANNEL_NUMS) > > +???????????????return 0; > > + > > +???????if (channel < channel_core) > > +???????????????return 0444; > > + > > +???????if (test_bit(channel - channel_core, priv->core_mask)) > > +???????????????return 0444; > > + > > +???????return 0; > > +} > > + > > +static int init_core_mask(struct peci_cputemp *priv) > > +{ > > +???????struct peci_device *peci_dev = priv->peci_dev; > > +???????struct resolved_cores_reg *reg = priv->gen_info->reg; > > +???????u64 core_mask; > > +???????u32 data; > > +???????int ret; > > + > > +???????/* Get the RESOLVED_CORES register value */ > > +???????switch (peci_dev->info.model) { > > +???????case INTEL_FAM6_ICELAKE_X: > > +???????case INTEL_FAM6_ICELAKE_D: > > +???????????????ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg- > > >dev, > > +??????????????????????????????????????????? reg->func, reg->offset + 4, > > &data); > > +???????????????if (ret) > > +???????????????????????return ret; > > + > > +???????????????core_mask = (u64)data << 32; > > + > > +???????????????ret = peci_ep_pci_local_read(peci_dev, 0, reg->bus, reg- > > >dev, > > +??????????????????????????????????????????? reg->func, reg->offset, &data); > > +???????????????if (ret) > > +???????????????????????return ret; > > + > > +???????????????core_mask |= data; > > + > > +???????????????break; > > +???????default: > > +???????????????ret = peci_pci_local_read(peci_dev, reg->bus, reg->dev, > > +???????????????????????????????????????? reg->func, reg->offset, &data); > > +???????????????if (ret) > > +???????????????????????return ret; > > + > > +???????????????core_mask = data; > > + > > +???????????????break; > > +???????} > > + > > +???????if (!core_mask) > > +???????????????return -EIO; > > + > > +???????bitmap_from_u64(priv->core_mask, core_mask); > > + > > +???????return 0; > > +} > > + > > +static int create_temp_label(struct peci_cputemp *priv) > > +{ > > +???????unsigned long core_max = find_last_bit(priv->core_mask, > > CORE_NUMS_MAX); > > +???????int i; > > + > > +???????priv->coretemp_label = devm_kzalloc(priv->dev, core_max * > > sizeof(char *), GFP_KERNEL); > > +???????if (!priv->coretemp_label) > > +???????????????return -ENOMEM; > > + > > +???????for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) { > > +???????????????priv->coretemp_label[i] = devm_kasprintf(priv->dev, > > GFP_KERNEL, "Core %d", i); > > +???????????????if (!priv->coretemp_label[i]) > > +???????????????????????return -ENOMEM; > > +???????} > > + > > +???????return 0; > > +} > > + > > +static void check_resolved_cores(struct peci_cputemp *priv) > > +{ > > +???????/* > > +??????? * Failure to resolve cores is non-critical, we're still able to > > +??????? * provide other sensor data. > > +??????? */ > > + > > +???????if (init_core_mask(priv)) > > +???????????????return; > > + > > +???????if (create_temp_label(priv)) > > +???????????????bitmap_zero(priv->core_mask, CORE_NUMS_MAX); > > +} > > + > > +static void sensor_init(struct peci_cputemp *priv) > > +{ > > +???????int i; > > + > > +???????mutex_init(&priv->temp.target.state.lock); > > +???????mutex_init(&priv->temp.die.state.lock); > > +???????mutex_init(&priv->temp.dts.state.lock); > > + > > +???????for_each_set_bit(i, priv->core_mask, CORE_NUMS_MAX) > > +???????????????mutex_init(&priv->temp.core[i].state.lock); > > +} > > + > > +static const struct hwmon_ops peci_cputemp_ops = { > > +???????.is_visible = cputemp_is_visible, > > +???????.read_string = cputemp_read_string, > > +???????.read = cputemp_read, > > +}; > > + > > +static const u32 peci_cputemp_temp_channel_config[] = { > > +???????/* Die temperature */ > > +???????HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | > > HWMON_T_CRIT_HYST, > > +???????/* DTS margin */ > > +???????HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_MAX | HWMON_T_CRIT | > > HWMON_T_CRIT_HYST, > > +???????/* Tcontrol temperature */ > > +???????HWMON_T_LABEL | HWMON_T_INPUT | HWMON_T_CRIT, > > +???????/* Tthrottle temperature */ > > +???????HWMON_T_LABEL | HWMON_T_INPUT, > > +???????/* Tjmax temperature */ > > +???????HWMON_T_LABEL | HWMON_T_INPUT, > > +???????/* Core temperature - for all core channels */ > > +???????[channel_core ... CPUTEMP_CHANNEL_NUMS - 1] = HWMON_T_LABEL | > > HWMON_T_INPUT, > > +???????0 > > +}; > > + > > +static const struct hwmon_channel_info peci_cputemp_temp_channel = { > > +???????.type = hwmon_temp, > > +???????.config = peci_cputemp_temp_channel_config, > > +}; > > + > > +static const struct hwmon_channel_info *peci_cputemp_info[] = { > > +???????&peci_cputemp_temp_channel, > > +???????NULL > > +}; > > + > > +static const struct hwmon_chip_info peci_cputemp_chip_info = { > > +???????.ops = &peci_cputemp_ops, > > +???????.info = peci_cputemp_info, > > +}; > > + > > +static int peci_cputemp_probe(struct auxiliary_device *adev, > > +???????????????????????????? const struct auxiliary_device_id *id) > > +{ > > +???????struct device *dev = &adev->dev; > > +???????struct peci_device *peci_dev = to_peci_device(dev->parent); > > +???????struct peci_cputemp *priv; > > +???????struct device *hwmon_dev; > > + > > +???????priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > > +???????if (!priv) > > +???????????????return -ENOMEM; > > + > > +???????priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_cputemp.cpu%d", > > +?????????????????????????????????? peci_dev->info.socket_id); > > +???????if (!priv->name) > > +???????????????return -ENOMEM; > > + > > +???????priv->dev = dev; > > +???????priv->peci_dev = peci_dev; > > +???????priv->gen_info = (const struct cpu_info *)id->driver_data; > > + > > +???????/* > > +??????? * This is just a sanity check. Since we're using commands that are > > +??????? * guaranteed to be supported on a given platform, we should never > > see > > +??????? * revision lower than expected. > > +??????? */ > > +???????if (peci_dev->info.peci_revision < priv->gen_info- > > >min_peci_revision) > > +???????????????dev_warn(priv->dev, > > +??????????????????????? "Unexpected PECI revision %#x, some features may be > > unavailable\n", > > +??????????????????????? peci_dev->info.peci_revision); > > + > > +???????check_resolved_cores(priv); > > + > > +???????sensor_init(priv); > > + > > +???????hwmon_dev = devm_hwmon_device_register_with_info(priv->dev, priv- > > >name, > > +??????????????????????????????????????????????????????? priv, > > &peci_cputemp_chip_info, NULL); > > + > > +???????return PTR_ERR_OR_ZERO(hwmon_dev); > > +} > > + > > +/* > > + * RESOLVED_CORES PCI configuration register may have different location on > > + * different platforms. > > + */ > > +static struct resolved_cores_reg resolved_cores_reg_hsx = { > > +???????.bus = 1, > > +???????.dev = 30, > > +???????.func = 3, > > +???????.offset = 0xb4, > > +}; > > + > > +static struct resolved_cores_reg resolved_cores_reg_icx = { > > +???????.bus = 14, > > +???????.dev = 30, > > +???????.func = 3, > > +???????.offset = 0xd0, > > +}; > > + > > +static const struct cpu_info cpu_hsx = { > > +???????.reg????????????= &resolved_cores_reg_hsx, > > +???????.min_peci_revision = 0x33, > > +}; > > + > > +static const struct cpu_info cpu_icx = { > > +???????.reg????????????= &resolved_cores_reg_icx, > > +???????.min_peci_revision = 0x40, > > +}; > > + > > +static const struct auxiliary_device_id peci_cputemp_ids[] = { > > +???????{ > > +???????????????.name = "peci_cpu.cputemp.hsx", > > +???????????????.driver_data = (kernel_ulong_t)&cpu_hsx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.cputemp.bdx", > > +???????????????.driver_data = (kernel_ulong_t)&cpu_hsx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.cputemp.bdxd", > > +???????????????.driver_data = (kernel_ulong_t)&cpu_hsx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.cputemp.skx", > > +???????????????.driver_data = (kernel_ulong_t)&cpu_hsx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.cputemp.icx", > > +???????????????.driver_data = (kernel_ulong_t)&cpu_icx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.cputemp.icxd", > > +???????????????.driver_data = (kernel_ulong_t)&cpu_icx, > > +???????}, > > +???????{ } > > +}; > > +MODULE_DEVICE_TABLE(auxiliary, peci_cputemp_ids); > > + > > +static struct auxiliary_driver peci_cputemp_driver = { > > +???????.probe??????????= peci_cputemp_probe, > > +???????.id_table???????= peci_cputemp_ids, > > +}; > > + > > +module_auxiliary_driver(peci_cputemp_driver); > > + > > +MODULE_AUTHOR("Jae Hyun Yoo "); > > +MODULE_AUTHOR("Iwona Winiarska "); > > +MODULE_DESCRIPTION("PECI cputemp driver"); > > +MODULE_LICENSE("GPL"); > > +MODULE_IMPORT_NS(PECI_CPU); > > > From iwona.winiarska at intel.com Wed Aug 4 20:46:06 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Wed, 4 Aug 2021 10:46:06 +0000 Subject: [PATCH v2 13/15] hwmon: peci: Add dimmtemp driver In-Reply-To: <20210803153937.GA337938@roeck-us.net> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-14-iwona.winiarska@intel.com> <20210803153937.GA337938@roeck-us.net> Message-ID: On Tue, 2021-08-03 at 08:39 -0700, Guenter Roeck wrote: > On Tue, Aug 03, 2021 at 01:31:32PM +0200, Iwona Winiarska wrote: > > Add peci-dimmtemp driver for Temperature Sensor on DIMM readings that > > are accessible via the processor PECI interface. > > > > The main use case for the driver (and PECI interface) is out-of-band > > management, where we're able to obtain thermal readings from an external > > entity connected with PECI, e.g. BMC on server platforms. > > > > Co-developed-by: Jae Hyun Yoo > > Signed-off-by: Jae Hyun Yoo > > Signed-off-by: Iwona Winiarska > > Reviewed-by: Pierre-Louis Bossart > > --- > > Note that the timeout was completely removed - we're going to probe > > for detected DIMMs every 5 seconds until we reach "stable" state of > > either getting correct DIMM data or getting all -EINVAL (which > > suggest that the CPU doesn't have any DIMMs). > > > > ?drivers/hwmon/peci/Kconfig??? |? 13 + > > ?drivers/hwmon/peci/Makefile?? |?? 2 + > > ?drivers/hwmon/peci/dimmtemp.c | 614 ++++++++++++++++++++++++++++++++++ > > ?3 files changed, 629 insertions(+) > > ?create mode 100644 drivers/hwmon/peci/dimmtemp.c > > > > diff --git a/drivers/hwmon/peci/Kconfig b/drivers/hwmon/peci/Kconfig > > index e10eed68d70a..9d32a57badfe 100644 > > --- a/drivers/hwmon/peci/Kconfig > > +++ b/drivers/hwmon/peci/Kconfig > > @@ -14,5 +14,18 @@ config SENSORS_PECI_CPUTEMP > > ????????? This driver can also be built as a module. If so, the module > > ????????? will be called peci-cputemp. > > ? > > +config SENSORS_PECI_DIMMTEMP > > +???????tristate "PECI DIMM temperature monitoring client" > > +???????depends on PECI > > +???????select SENSORS_PECI > > +???????select PECI_CPU > > +???????help > > +???????? If you say yes here you get support for the generic Intel PECI > > hwmon > > +???????? driver which provides Temperature Sensor on DIMM readings that are > > +???????? accessible via the processor PECI interface. > > + > > +???????? This driver can also be built as a module. If so, the module > > +???????? will be called peci-dimmtemp. > > + > > ?config SENSORS_PECI > > ????????tristate > > diff --git a/drivers/hwmon/peci/Makefile b/drivers/hwmon/peci/Makefile > > index e8a0ada5ab1f..191cfa0227f3 100644 > > --- a/drivers/hwmon/peci/Makefile > > +++ b/drivers/hwmon/peci/Makefile > > @@ -1,5 +1,7 @@ > > ?# SPDX-License-Identifier: GPL-2.0-only > > ? > > ?peci-cputemp-y := cputemp.o > > +peci-dimmtemp-y := dimmtemp.o > > ? > > ?obj-$(CONFIG_SENSORS_PECI_CPUTEMP)?????+= peci-cputemp.o > > +obj-$(CONFIG_SENSORS_PECI_DIMMTEMP)????+= peci-dimmtemp.o > > diff --git a/drivers/hwmon/peci/dimmtemp.c b/drivers/hwmon/peci/dimmtemp.c > > new file mode 100644 > > index 000000000000..6264c29bb6c0 > > --- /dev/null > > +++ b/drivers/hwmon/peci/dimmtemp.c > > @@ -0,0 +1,614 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +// Copyright (c) 2018-2021 Intel Corporation > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include "common.h" > > + > > +#define DIMM_MASK_CHECK_DELAY_JIFFIES??msecs_to_jiffies(5000) > > + > > +/* Max number of channel ranks and DIMM index per channel */ > > +#define CHAN_RANK_MAX_ON_HSX???8 > > +#define DIMM_IDX_MAX_ON_HSX????3 > > +#define CHAN_RANK_MAX_ON_BDX???4 > > +#define DIMM_IDX_MAX_ON_BDX????3 > > +#define CHAN_RANK_MAX_ON_BDXD??2 > > +#define DIMM_IDX_MAX_ON_BDXD???2 > > +#define CHAN_RANK_MAX_ON_SKX???6 > > +#define DIMM_IDX_MAX_ON_SKX????2 > > +#define CHAN_RANK_MAX_ON_ICX???8 > > +#define DIMM_IDX_MAX_ON_ICX????2 > > +#define CHAN_RANK_MAX_ON_ICXD??4 > > +#define DIMM_IDX_MAX_ON_ICXD???2 > > + > > +#define CHAN_RANK_MAX??????????CHAN_RANK_MAX_ON_HSX > > +#define DIMM_IDX_MAX???????????DIMM_IDX_MAX_ON_HSX > > +#define DIMM_NUMS_MAX??????????(CHAN_RANK_MAX * DIMM_IDX_MAX) > > + > > +#define CPU_SEG_MASK???????????GENMASK(23, 16) > > +#define GET_CPU_SEG(x)?????????(((x) & CPU_SEG_MASK) >> 16) > > +#define CPU_BUS_MASK???????????GENMASK(7, 0) > > +#define GET_CPU_BUS(x)?????????((x) & CPU_BUS_MASK) > > + > > +#define DIMM_TEMP_MAX??????????GENMASK(15, 8) > > +#define DIMM_TEMP_CRIT?????????GENMASK(23, 16) > > +#define GET_TEMP_MAX(x)????????????????(((x) & DIMM_TEMP_MAX) >> 8) > > +#define GET_TEMP_CRIT(x)???????(((x) & DIMM_TEMP_CRIT) >> 16) > > + > > +struct peci_dimmtemp; > > + > > +struct dimm_info { > > +???????int chan_rank_max; > > +???????int dimm_idx_max; > > +???????u8 min_peci_revision; > > +???????int (*read_thresholds)(struct peci_dimmtemp *priv, int dimm_order, > > +????????????????????????????? int chan_rank, u32 *data); > > +}; > > + > > +struct peci_dimm_thresholds { > > +???????long temp_max; > > +???????long temp_crit; > > +???????struct peci_sensor_state state; > > +}; > > + > > +enum peci_dimm_threshold_type { > > +???????temp_max_type, > > +???????temp_crit_type, > > +}; > > + > > +struct peci_dimmtemp { > > +???????struct peci_device *peci_dev; > > +???????struct device *dev; > > +???????const char *name; > > +???????const struct dimm_info *gen_info; > > +???????struct delayed_work detect_work; > > +???????struct { > > +???????????????struct peci_sensor_data temp; > > +???????????????struct peci_dimm_thresholds thresholds; > > +???????} dimm[DIMM_NUMS_MAX]; > > +???????char **dimmtemp_label; > > +???????DECLARE_BITMAP(dimm_mask, DIMM_NUMS_MAX); > > +}; > > + > > +static u8 __dimm_temp(u32 reg, int dimm_order) > > +{ > > +???????return (reg >> (dimm_order * 8)) & 0xff; > > +} > > + > > +static int get_dimm_temp(struct peci_dimmtemp *priv, int dimm_no, long > > *val) > > +{ > > +???????int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; > > +???????int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; > > +???????u32 data; > > +???????int ret; > > ????????int ret = 0; > > > + > > +???????mutex_lock(&priv->dimm[dimm_no].temp.state.lock); > > +???????if (!peci_sensor_need_update(&priv->dimm[dimm_no].temp.state)) > > +???????????????goto skip_update; > > + > > +???????ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, > > chan_rank, &data); > > +???????if (ret) { > > +???????????????mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); > > +???????????????return ret; > > +???????} > > ????????if (ret) > ????????????????goto unlock; > > > + > > +???????priv->dimm[dimm_no].temp.value = __dimm_temp(data, dimm_order) * > > MILLIDEGREE_PER_DEGREE; > > + > > +???????peci_sensor_mark_updated(&priv->dimm[dimm_no].temp.state); > > + > > +skip_update: > > +???????*val = priv->dimm[dimm_no].temp.value; > > unlock: > > +???????mutex_unlock(&priv->dimm[dimm_no].temp.state.lock); > > +???????return 0; > > ????????return ret; Ack. > > > +} > > + > > +static int update_thresholds(struct peci_dimmtemp *priv, int dimm_no) > > +{ > > +???????int dimm_order = dimm_no % priv->gen_info->dimm_idx_max; > > +???????int chan_rank = dimm_no / priv->gen_info->dimm_idx_max; > > +???????u32 data; > > +???????int ret; > > + > > +???????if (!peci_sensor_need_update(&priv->dimm[dimm_no].thresholds.state)) > > +???????????????return 0; > > + > > +???????ret = priv->gen_info->read_thresholds(priv, dimm_order, chan_rank, > > &data); > > +???????if (ret == -ENODATA) /* Use default or previous value */ > > +???????????????return 0; > > +???????if (ret) > > +???????????????return ret; > > + > > +???????priv->dimm[dimm_no].thresholds.temp_max = GET_TEMP_MAX(data) * > > MILLIDEGREE_PER_DEGREE; > > +???????priv->dimm[dimm_no].thresholds.temp_crit = GET_TEMP_CRIT(data) * > > MILLIDEGREE_PER_DEGREE; > > + > > +???????peci_sensor_mark_updated(&priv->dimm[dimm_no].thresholds.state); > > + > > +???????return 0; > > +} > > + > > +static int get_dimm_thresholds(struct peci_dimmtemp *priv, enum > > peci_dimm_threshold_type type, > > +????????????????????????????? int dimm_no, long *val) > > +{ > > +???????int ret; > > + > > +???????mutex_lock(&priv->dimm[dimm_no].thresholds.state.lock); > > +???????ret = update_thresholds(priv, dimm_no); > > +???????if (ret) > > +???????????????goto unlock; > > + > > +???????switch (type) { > > +???????case temp_max_type: > > +???????????????*val = priv->dimm[dimm_no].thresholds.temp_max; > > +???????????????break; > > +???????case temp_crit_type: > > +???????????????*val = priv->dimm[dimm_no].thresholds.temp_crit; > > +???????????????break; > > +???????default: > > +???????????????ret = -EOPNOTSUPP; > > +???????????????break; > > +???????} > > +unlock: > > +???????mutex_unlock(&priv->dimm[dimm_no].thresholds.state.lock); > > + > > +???????return ret; > > +} > > + > > +static int dimmtemp_read_string(struct device *dev, > > +???????????????????????????????enum hwmon_sensor_types type, > > +???????????????????????????????u32 attr, int channel, const char **str) > > +{ > > +???????struct peci_dimmtemp *priv = dev_get_drvdata(dev); > > + > > +???????if (attr != hwmon_temp_label) > > +???????????????return -EOPNOTSUPP; > > + > > +???????*str = (const char *)priv->dimmtemp_label[channel]; > > + > > +???????return 0; > > +} > > + > > +static int dimmtemp_read(struct device *dev, enum hwmon_sensor_types type, > > +??????????????????????? u32 attr, int channel, long *val) > > +{ > > +???????struct peci_dimmtemp *priv = dev_get_drvdata(dev); > > + > > +???????switch (attr) { > > +???????case hwmon_temp_input: > > +???????????????return get_dimm_temp(priv, channel, val); > > +???????case hwmon_temp_max: > > +???????????????return get_dimm_thresholds(priv, temp_max_type, channel, > > val); > > +???????case hwmon_temp_crit: > > +???????????????return get_dimm_thresholds(priv, temp_crit_type, channel, > > val); > > +???????default: > > +???????????????break; > > +???????} > > + > > +???????return -EOPNOTSUPP; > > +} > > + > > +static umode_t dimmtemp_is_visible(const void *data, enum > > hwmon_sensor_types type, > > +????????????????????????????????? u32 attr, int channel) > > +{ > > +???????const struct peci_dimmtemp *priv = data; > > + > > +???????if (test_bit(channel, priv->dimm_mask)) > > +???????????????return 0444; > > + > > +???????return 0; > > +} > > + > > +static const struct hwmon_ops peci_dimmtemp_ops = { > > +???????.is_visible = dimmtemp_is_visible, > > +???????.read_string = dimmtemp_read_string, > > +???????.read = dimmtemp_read, > > +}; > > + > > +static int check_populated_dimms(struct peci_dimmtemp *priv) > > +{ > > +???????int chan_rank_max = priv->gen_info->chan_rank_max; > > +???????int dimm_idx_max = priv->gen_info->dimm_idx_max; > > +???????u32 chan_rank_empty = 0; > > +???????u64 dimm_mask = 0; > > +???????int chan_rank, dimm_idx, ret; > > +???????u32 pcs; > > + > > +???????BUILD_BUG_ON(CHAN_RANK_MAX > 32); > > +???????BUILD_BUG_ON(DIMM_NUMS_MAX > 64); > > I don't immediately see the value of those build bugs. What happens if > CHAN_RANK_MAX > 32 or DIMM_NUMS_MAX > 64 ? Where do those limits come > from ? Supported HW doesn't come near the limit for now - it's just an "artificial" limit imposed by variables we're using (u64 for dimm_mask and u32 for chan_rank_empty). > > > +???????if (chan_rank_max * dimm_idx_max > DIMM_NUMS_MAX) { > > +???????????????WARN_ONCE(1, "Unsupported number of DIMMs"); > > Maybe display the values (chan_rank_max and dimm_idx_max). Ok. > > > +???????????????return -EINVAL; > > +???????} > > + > > +???????for (chan_rank = 0; chan_rank < chan_rank_max; chan_rank++) { > > +???????????????ret = peci_pcs_read(priv->peci_dev, PECI_PCS_DDR_DIMM_TEMP, > > chan_rank, &pcs); > > +???????????????if (ret) { > > +???????????????????????/* > > +??????????????????????? * Overall, we expect either success or -EINVAL in > > +??????????????????????? * order to determine whether DIMM is populated or > > not. > > +??????????????????????? * For anything else - we fall back to defering the > > Why " - " ? Hum... No idea after reading it now. I'll drop it. Thank you -Iwona > > > +??????????????????????? * detection to be performed at a later point in > > time. > > +??????????????????????? */ > > +???????????????????????if (ret == -EINVAL) { > > +???????????????????????????????chan_rank_empty |= BIT(chan_rank); > > +???????????????????????????????continue; > > +???????????????????????} > > + > > +???????????????????????return -EAGAIN; > > +???????????????} > > + > > +???????????????for (dimm_idx = 0; dimm_idx < dimm_idx_max; dimm_idx++) > > +???????????????????????if (__dimm_temp(pcs, dimm_idx)) > > +???????????????????????????????dimm_mask |= BIT(chan_rank * dimm_idx_max + > > dimm_idx); > > +???????} > > + > > +???????/* If we got all -EINVALs, it means that the CPU doesn't have any > > DIMMs. */ > > +???????if (chan_rank_empty == GENMASK(chan_rank_max - 1, 0)) > > +???????????????return -ENODEV; > > + > > +???????/* > > +??????? * It's possible that memory training is not done yet. In this case > > we > > +??????? * defer the detection to be performed at a later point in time. > > +??????? */ > > +???????if (!dimm_mask) > > +???????????????return -EAGAIN; > > + > > +???????dev_dbg(priv->dev, "Scanned populated DIMMs: %#llx\n", dimm_mask); > > + > > +???????bitmap_from_u64(priv->dimm_mask, dimm_mask); > > + > > +???????return 0; > > +} > > + > > +static int create_dimm_temp_label(struct peci_dimmtemp *priv, int chan) > > +{ > > +???????int rank = chan / priv->gen_info->dimm_idx_max; > > +???????int idx = chan % priv->gen_info->dimm_idx_max; > > + > > +???????priv->dimmtemp_label[chan] = devm_kasprintf(priv->dev, GFP_KERNEL, > > +?????????????????????????????????????????????????? "DIMM %c%d", 'A' + rank, > > +?????????????????????????????????????????????????? idx + 1); > > +???????if (!priv->dimmtemp_label[chan]) > > +???????????????return -ENOMEM; > > + > > +???????return 0; > > +} > > + > > +static const u32 peci_dimmtemp_temp_channel_config[] = { > > +???????[0 ... DIMM_NUMS_MAX - 1] = HWMON_T_LABEL | HWMON_T_INPUT | > > HWMON_T_MAX | HWMON_T_CRIT, > > +???????0 > > +}; > > + > > +static const struct hwmon_channel_info peci_dimmtemp_temp_channel = { > > +???????.type = hwmon_temp, > > +???????.config = peci_dimmtemp_temp_channel_config, > > +}; > > + > > +static const struct hwmon_channel_info *peci_dimmtemp_temp_info[] = { > > +???????&peci_dimmtemp_temp_channel, > > +???????NULL > > +}; > > + > > +static const struct hwmon_chip_info peci_dimmtemp_chip_info = { > > +???????.ops = &peci_dimmtemp_ops, > > +???????.info = peci_dimmtemp_temp_info, > > +}; > > + > > +static int create_dimm_temp_info(struct peci_dimmtemp *priv) > > +{ > > +???????int ret, i, channels; > > +???????struct device *dev; > > + > > +???????/* > > +??????? * We expect to either find populated DIMMs and carry on with > > creating > > +??????? * sensors, or find out that there are no DIMMs populated. > > +??????? * All other states mean that the platform never reached the state > > that > > +??????? * allows to check DIMM state - causing us to retry later on. > > +??????? */ > > +???????ret = check_populated_dimms(priv); > > +???????if (ret == -ENODEV) { > > +???????????????dev_dbg(priv->dev, "No DIMMs found\n"); > > +???????????????return 0; > > +???????} else if (ret) { > > +???????????????schedule_delayed_work(&priv->detect_work, > > DIMM_MASK_CHECK_DELAY_JIFFIES); > > +???????????????dev_dbg(priv->dev, "Deferred populating DIMM temp info\n"); > > +???????????????return ret; > > +???????} > > + > > +???????channels = priv->gen_info->chan_rank_max * priv->gen_info- > > >dimm_idx_max; > > + > > +???????priv->dimmtemp_label = devm_kzalloc(priv->dev, channels * > > sizeof(char *), GFP_KERNEL); > > +???????if (!priv->dimmtemp_label) > > +???????????????return -ENOMEM; > > + > > +???????for_each_set_bit(i, priv->dimm_mask, DIMM_NUMS_MAX) { > > +???????????????ret = create_dimm_temp_label(priv, i); > > +???????????????if (ret) > > +???????????????????????return ret; > > +???????????????mutex_init(&priv->dimm[i].thresholds.state.lock); > > +???????????????mutex_init(&priv->dimm[i].temp.state.lock); > > +???????} > > + > > +???????dev = devm_hwmon_device_register_with_info(priv->dev, priv->name, > > priv, > > +????????????????????????????????????????????????? &peci_dimmtemp_chip_info, > > NULL); > > +???????if (IS_ERR(dev)) { > > +???????????????dev_err(priv->dev, "Failed to register hwmon device\n"); > > +???????????????return PTR_ERR(dev); > > +???????} > > + > > +???????dev_dbg(priv->dev, "%s: sensor '%s'\n", dev_name(dev), priv->name); > > + > > +???????return 0; > > +} > > + > > +static void create_dimm_temp_info_delayed(struct work_struct *work) > > +{ > > +???????struct peci_dimmtemp *priv = container_of(to_delayed_work(work), > > +???????????????????????????????????????????????? struct peci_dimmtemp, > > +???????????????????????????????????????????????? detect_work); > > +???????int ret; > > + > > +???????ret = create_dimm_temp_info(priv); > > +???????if (ret && ret != -EAGAIN) > > +???????????????dev_err(priv->dev, "Failed to populate DIMM temp info\n"); > > +} > > + > > +static void remove_delayed_work(void *_priv) > > +{ > > +???????struct peci_dimmtemp *priv = _priv; > > + > > +???????cancel_delayed_work_sync(&priv->detect_work); > > +} > > + > > +static int peci_dimmtemp_probe(struct auxiliary_device *adev, const struct > > auxiliary_device_id *id) > > +{ > > +???????struct device *dev = &adev->dev; > > +???????struct peci_device *peci_dev = to_peci_device(dev->parent); > > +???????struct peci_dimmtemp *priv; > > +???????int ret; > > + > > +???????priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); > > +???????if (!priv) > > +???????????????return -ENOMEM; > > + > > +???????priv->name = devm_kasprintf(dev, GFP_KERNEL, "peci_dimmtemp.cpu%d", > > +?????????????????????????????????? peci_dev->info.socket_id); > > +???????if (!priv->name) > > +???????????????return -ENOMEM; > > + > > +???????priv->dev = dev; > > +???????priv->peci_dev = peci_dev; > > +???????priv->gen_info = (const struct dimm_info *)id->driver_data; > > + > > +???????/* > > +??????? * This is just a sanity check. Since we're using commands that are > > +??????? * guaranteed to be supported on a given platform, we should never > > see > > +??????? * revision lower than expected. > > +??????? */ > > +???????if (peci_dev->info.peci_revision < priv->gen_info- > > >min_peci_revision) > > +???????????????dev_warn(priv->dev, > > +??????????????????????? "Unexpected PECI revision %#x, some features may be > > unavailable\n", > > +??????????????????????? peci_dev->info.peci_revision); > > + > > +???????INIT_DELAYED_WORK(&priv->detect_work, > > create_dimm_temp_info_delayed); > > + > > +???????ret = devm_add_action_or_reset(priv->dev, remove_delayed_work, > > priv); > > +???????if (ret) > > +???????????????return ret; > > + > > +???????ret = create_dimm_temp_info(priv); > > +???????if (ret && ret != -EAGAIN) { > > +???????????????dev_err(dev, "Failed to populate DIMM temp info\n"); > > +???????????????return ret; > > +???????} > > + > > +???????return 0; > > +} > > + > > +static int > > +read_thresholds_hsx(struct peci_dimmtemp *priv, int dimm_order, int > > chan_rank, u32 *data) > > +{ > > +???????u8 dev, func; > > +???????u16 reg; > > +???????int ret; > > + > > +???????/* > > +??????? * Device 20, Function 0: IMC 0 channel 0 -> rank 0 > > +??????? * Device 20, Function 1: IMC 0 channel 1 -> rank 1 > > +??????? * Device 21, Function 0: IMC 0 channel 2 -> rank 2 > > +??????? * Device 21, Function 1: IMC 0 channel 3 -> rank 3 > > +??????? * Device 23, Function 0: IMC 1 channel 0 -> rank 4 > > +??????? * Device 23, Function 1: IMC 1 channel 1 -> rank 5 > > +??????? * Device 24, Function 0: IMC 1 channel 2 -> rank 6 > > +??????? * Device 24, Function 1: IMC 1 channel 3 -> rank 7 > > +??????? */ > > +???????dev = 20 + chan_rank / 2 + chan_rank / 4; > > +???????func = chan_rank % 2; > > +???????reg = 0x120 + dimm_order * 4; > > + > > +???????ret = peci_pci_local_read(priv->peci_dev, 1, dev, func, reg, data); > > +???????if (ret) > > +???????????????return ret; > > + > > +???????return 0; > > +} > > + > > +static int > > +read_thresholds_bdxd(struct peci_dimmtemp *priv, int dimm_order, int > > chan_rank, u32 *data) > > +{ > > +???????u8 dev, func; > > +???????u16 reg; > > +???????int ret; > > + > > +???????/* > > +??????? * Device 10, Function 2: IMC 0 channel 0 -> rank 0 > > +??????? * Device 10, Function 6: IMC 0 channel 1 -> rank 1 > > +??????? * Device 12, Function 2: IMC 1 channel 0 -> rank 2 > > +??????? * Device 12, Function 6: IMC 1 channel 1 -> rank 3 > > +??????? */ > > +???????dev = 10 + chan_rank / 2 * 2; > > +???????func = (chan_rank % 2) ? 6 : 2; > > +???????reg = 0x120 + dimm_order * 4; > > + > > +???????ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); > > +???????if (ret) > > +???????????????return ret; > > + > > +???????return 0; > > +} > > + > > +static int > > +read_thresholds_skx(struct peci_dimmtemp *priv, int dimm_order, int > > chan_rank, u32 *data) > > +{ > > +???????u8 dev, func; > > +???????u16 reg; > > +???????int ret; > > + > > +???????/* > > +??????? * Device 10, Function 2: IMC 0 channel 0 -> rank 0 > > +??????? * Device 10, Function 6: IMC 0 channel 1 -> rank 1 > > +??????? * Device 11, Function 2: IMC 0 channel 2 -> rank 2 > > +??????? * Device 12, Function 2: IMC 1 channel 0 -> rank 3 > > +??????? * Device 12, Function 6: IMC 1 channel 1 -> rank 4 > > +??????? * Device 13, Function 2: IMC 1 channel 2 -> rank 5 > > +??????? */ > > +???????dev = 10 + chan_rank / 3 * 2 + (chan_rank % 3 == 2 ? 1 : 0); > > +???????func = chan_rank % 3 == 1 ? 6 : 2; > > +???????reg = 0x120 + dimm_order * 4; > > + > > +???????ret = peci_pci_local_read(priv->peci_dev, 2, dev, func, reg, data); > > +???????if (ret) > > +???????????????return ret; > > + > > +???????return 0; > > +} > > + > > +static int > > +read_thresholds_icx(struct peci_dimmtemp *priv, int dimm_order, int > > chan_rank, u32 *data) > > +{ > > +???????u32 reg_val; > > +???????u64 offset; > > +???????int ret; > > +???????u8 dev; > > + > > +???????ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd4, > > ®_val); > > +???????if (ret || !(reg_val & BIT(31))) > > +???????????????return -ENODATA; /* Use default or previous value */ > > + > > +???????ret = peci_ep_pci_local_read(priv->peci_dev, 0, 13, 0, 2, 0xd0, > > ®_val); > > +???????if (ret) > > +???????????????return -ENODATA; /* Use default or previous value */ > > + > > +???????/* > > +??????? * Device 26, Offset 224e0: IMC 0 channel 0 -> rank 0 > > +??????? * Device 26, Offset 264e0: IMC 0 channel 1 -> rank 1 > > +??????? * Device 27, Offset 224e0: IMC 1 channel 0 -> rank 2 > > +??????? * Device 27, Offset 264e0: IMC 1 channel 1 -> rank 3 > > +??????? * Device 28, Offset 224e0: IMC 2 channel 0 -> rank 4 > > +??????? * Device 28, Offset 264e0: IMC 2 channel 1 -> rank 5 > > +??????? * Device 29, Offset 224e0: IMC 3 channel 0 -> rank 6 > > +??????? * Device 29, Offset 264e0: IMC 3 channel 1 -> rank 7 > > +??????? */ > > +???????dev = 26 + chan_rank / 2; > > +???????offset = 0x224e0 + dimm_order * 4 + (chan_rank % 2) * 0x4000; > > + > > +???????ret = peci_mmio_read(priv->peci_dev, 0, GET_CPU_SEG(reg_val), > > GET_CPU_BUS(reg_val), > > +??????????????????????????? dev, 0, offset, data); > > +???????if (ret) > > +???????????????return ret; > > + > > +???????return 0; > > +} > > + > > +static const struct dimm_info dimm_hsx = { > > +???????.chan_rank_max??= CHAN_RANK_MAX_ON_HSX, > > +???????.dimm_idx_max???= DIMM_IDX_MAX_ON_HSX, > > +???????.min_peci_revision = 0x33, > > +???????.read_thresholds = &read_thresholds_hsx, > > +}; > > + > > +static const struct dimm_info dimm_bdx = { > > +???????.chan_rank_max??= CHAN_RANK_MAX_ON_BDX, > > +???????.dimm_idx_max???= DIMM_IDX_MAX_ON_BDX, > > +???????.min_peci_revision = 0x33, > > +???????.read_thresholds = &read_thresholds_hsx, > > +}; > > + > > +static const struct dimm_info dimm_bdxd = { > > +???????.chan_rank_max??= CHAN_RANK_MAX_ON_BDXD, > > +???????.dimm_idx_max???= DIMM_IDX_MAX_ON_BDXD, > > +???????.min_peci_revision = 0x33, > > +???????.read_thresholds = &read_thresholds_bdxd, > > +}; > > + > > +static const struct dimm_info dimm_skx = { > > +???????.chan_rank_max??= CHAN_RANK_MAX_ON_SKX, > > +???????.dimm_idx_max???= DIMM_IDX_MAX_ON_SKX, > > +???????.min_peci_revision = 0x33, > > +???????.read_thresholds = &read_thresholds_skx, > > +}; > > + > > +static const struct dimm_info dimm_icx = { > > +???????.chan_rank_max??= CHAN_RANK_MAX_ON_ICX, > > +???????.dimm_idx_max???= DIMM_IDX_MAX_ON_ICX, > > +???????.min_peci_revision = 0x40, > > +???????.read_thresholds = &read_thresholds_icx, > > +}; > > + > > +static const struct dimm_info dimm_icxd = { > > +???????.chan_rank_max??= CHAN_RANK_MAX_ON_ICXD, > > +???????.dimm_idx_max???= DIMM_IDX_MAX_ON_ICXD, > > +???????.min_peci_revision = 0x40, > > +???????.read_thresholds = &read_thresholds_icx, > > +}; > > + > > +static const struct auxiliary_device_id peci_dimmtemp_ids[] = { > > +???????{ > > +???????????????.name = "peci_cpu.dimmtemp.hsx", > > +???????????????.driver_data = (kernel_ulong_t)&dimm_hsx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.dimmtemp.bdx", > > +???????????????.driver_data = (kernel_ulong_t)&dimm_bdx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.dimmtemp.bdxd", > > +???????????????.driver_data = (kernel_ulong_t)&dimm_bdxd, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.dimmtemp.skx", > > +???????????????.driver_data = (kernel_ulong_t)&dimm_skx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.dimmtemp.icx", > > +???????????????.driver_data = (kernel_ulong_t)&dimm_icx, > > +???????}, > > +???????{ > > +???????????????.name = "peci_cpu.dimmtemp.icxd", > > +???????????????.driver_data = (kernel_ulong_t)&dimm_icxd, > > +???????}, > > +???????{ } > > +}; > > +MODULE_DEVICE_TABLE(auxiliary, peci_dimmtemp_ids); > > + > > +static struct auxiliary_driver peci_dimmtemp_driver = { > > +???????.probe??????????= peci_dimmtemp_probe, > > +???????.id_table???????= peci_dimmtemp_ids, > > +}; > > + > > +module_auxiliary_driver(peci_dimmtemp_driver); > > + > > +MODULE_AUTHOR("Jae Hyun Yoo "); > > +MODULE_AUTHOR("Iwona Winiarska "); > > +MODULE_DESCRIPTION("PECI dimmtemp driver"); > > +MODULE_LICENSE("GPL"); > > +MODULE_IMPORT_NS(PECI_CPU); From linus.walleij at linaro.org Tue Aug 10 23:30:13 2021 From: linus.walleij at linaro.org (Linus Walleij) Date: Tue, 10 Aug 2021 15:30:13 +0200 Subject: [PATCH] pinctrl: aspeed: placate kernel-doc warnings In-Reply-To: <20210723034840.8752-1-rdunlap@infradead.org> References: <20210723034840.8752-1-rdunlap@infradead.org> Message-ID: On Fri, Jul 23, 2021 at 5:49 AM Randy Dunlap wrote: > Eliminate kernel-doc warnings in drivers/pinctrl/aspeed by using > proper kernel-doc notation. > > Fixes these kernel-doc warnings: > > drivers/pinctrl/aspeed/pinmux-aspeed.c:61: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst > * Query the enabled or disabled state for a mux function's signal on a pin > drivers/pinctrl/aspeed/pinctrl-aspeed.c:135: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst > * Search for the signal expression needed to enable the pin's signal for the > > Signed-off-by: Randy Dunlap > Reported-by: kernel test robot > Cc: Aditya Srivastava > Cc: Andrew Jeffery > Cc: linux-aspeed at lists.ozlabs.org > Cc: openbmc at lists.ozlabs.org > Cc: Linus Walleij > Cc: linux-gpio at vger.kernel.org Patch applied. Yours, Linus Walleij From linus.walleij at linaro.org Tue Aug 10 23:34:39 2021 From: linus.walleij at linaro.org (Linus Walleij) Date: Tue, 10 Aug 2021 15:34:39 +0200 Subject: [RFC PATCH 1/6] pinctrl: Add pinctrl_gpio_as_pin() In-Reply-To: <20210723075858.376378-2-andrew@aj.id.au> References: <20210723075858.376378-1-andrew@aj.id.au> <20210723075858.376378-2-andrew@aj.id.au> Message-ID: On Fri, Jul 23, 2021 at 9:59 AM Andrew Jeffery wrote: > Allow gpiochips to map the GPIO numberspace onto a pin numberspace when > the register layout for GPIO control is implemented in terms of the > pin numberspace. > > This requirement sounds kind of strange, but the patch is driven by > trying to resolve a bug in the leds-pca955x driver where this mapping is > not correctly performed. > > Signed-off-by: Andrew Jeffery (...) Hm this looks a bit strange... > +int pinctrl_gpio_as_pin(struct pinctrl_dev *pctldev, unsigned int gpio) This is not a good name for this function. Try to come up with a name that says exactly what the function does. E.g. "apple pear as apple slice" isn't very helpful, the use case for this is really hard to understand. > +EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin); This looks completely wrong. Yours, Linus Walleij From linus.walleij at linaro.org Tue Aug 10 23:54:11 2021 From: linus.walleij at linaro.org (Linus Walleij) Date: Tue, 10 Aug 2021 15:54:11 +0200 Subject: [RFC PATCH 4/6] leds: pca955x: Use pinctrl to map GPIOs to pins In-Reply-To: <20210723075858.376378-5-andrew@aj.id.au> References: <20210723075858.376378-1-andrew@aj.id.au> <20210723075858.376378-5-andrew@aj.id.au> Message-ID: On Fri, Jul 23, 2021 at 9:59 AM Andrew Jeffery wrote: > The leds-pca955x driver currently assumes that the GPIO numberspace and > the pin numberspace are the same. This quickly falls apart with a > devicetree binding such as the following: (...) Honestly I do not understand this patch. It seems to implement a pin controller and using it in nonstandard ways. If something implements the pin controller driver API it should be used as such IMO, externally. This seems to be using it do relay calls to itself which seems complicated, just invent something locally in the driver in that case? No need to use pin control? Can you explain why this LED driver needs to implement a pin controller? Yours, Linus Walleij From andrew at aj.id.au Wed Aug 11 10:19:05 2021 From: andrew at aj.id.au (Andrew Jeffery) Date: Wed, 11 Aug 2021 09:49:05 +0930 Subject: [RFC PATCH 4/6] leds: pca955x: Use pinctrl to map GPIOs to pins In-Reply-To: References: <20210723075858.376378-1-andrew@aj.id.au> <20210723075858.376378-5-andrew@aj.id.au> Message-ID: <96e52916-f113-4a91-b83f-e0de144611ca@www.fastmail.com> On Tue, 10 Aug 2021, at 23:24, Linus Walleij wrote: > On Fri, Jul 23, 2021 at 9:59 AM Andrew Jeffery wrote: > > > The leds-pca955x driver currently assumes that the GPIO numberspace and > > the pin numberspace are the same. This quickly falls apart with a > > devicetree binding such as the following: > (...) > > Honestly I do not understand this patch. It seems to implement a pin > controller and using it in nonstandard ways. Yeah, it's a bit abusive, hence RFC :) > > If something implements the pin controller driver API it should be > used as such IMO, externally. This seems to be using it do relay > calls to itself which seems complicated, just invent something > locally in the driver in that case? No need to use pin control? Right. After discussions with Andy I'm going to rework the approach to GPIOs which will remove a lot of complexity. The thought was to try to maintain the intent of the devicetree binding and use existing APIs, but all-in-all it's ended up twisting things up in knots a fair bit. We discard a lot of it by making the gpiochip always cover all pins and track use directly in the driver. > > Can you explain why this LED driver needs to implement a pin > controller? The short answer is it doesn't as it has none of the associated hardware. I'll cook up something simpler with the aim to avoid non-standard (or any) pinctrl. Andrew From andrew at aj.id.au Wed Aug 11 10:24:53 2021 From: andrew at aj.id.au (Andrew Jeffery) Date: Wed, 11 Aug 2021 09:54:53 +0930 Subject: [RFC PATCH 1/6] pinctrl: Add pinctrl_gpio_as_pin() In-Reply-To: References: <20210723075858.376378-1-andrew@aj.id.au> <20210723075858.376378-2-andrew@aj.id.au> Message-ID: <53199b3e-a711-4071-96a3-7836d3323906@www.fastmail.com> On Tue, 10 Aug 2021, at 23:04, Linus Walleij wrote: > On Fri, Jul 23, 2021 at 9:59 AM Andrew Jeffery wrote: > > > Allow gpiochips to map the GPIO numberspace onto a pin numberspace when > > the register layout for GPIO control is implemented in terms of the > > pin numberspace. > > > > This requirement sounds kind of strange, but the patch is driven by > > trying to resolve a bug in the leds-pca955x driver where this mapping is > > not correctly performed. > > > > Signed-off-by: Andrew Jeffery > > (...) > > Hm this looks a bit strange... > > > +int pinctrl_gpio_as_pin(struct pinctrl_dev *pctldev, unsigned int gpio) > > This is not a good name for this function. Try to come up with > a name that says exactly what the function does. > > E.g. "apple pear as apple slice" isn't very helpful, the use case for > this is really hard to understand. That's probably because I shouldn't be trying to do what I'm doing :) I'll stop doing that (i.e. rework patch 4/6) and this will go away entirely. > > > +EXPORT_SYMBOL_GPL(pinctrl_find_gpio_range_from_pin); > > This looks completely wrong. Yeah, whoops. That was an oversight while iterating on the patch. Andrew From robh at kernel.org Thu Aug 12 04:11:22 2021 From: robh at kernel.org (Rob Herring) Date: Wed, 11 Aug 2021 12:11:22 -0600 Subject: [PATCH v2 03/15] dt-bindings: Add generic bindings for PECI In-Reply-To: <20210803113134.2262882-4-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-4-iwona.winiarska@intel.com> Message-ID: On Tue, 03 Aug 2021 13:31:22 +0200, Iwona Winiarska wrote: > Add device tree bindings for the PECI controller. > > Signed-off-by: Iwona Winiarska > --- > .../bindings/peci/peci-controller.yaml | 33 +++++++++++++++++++ > 1 file changed, 33 insertions(+) > create mode 100644 Documentation/devicetree/bindings/peci/peci-controller.yaml > Reviewed-by: Rob Herring From robh at kernel.org Thu Aug 12 04:11:48 2021 From: robh at kernel.org (Rob Herring) Date: Wed, 11 Aug 2021 12:11:48 -0600 Subject: [PATCH v2 04/15] dt-bindings: Add bindings for peci-aspeed In-Reply-To: <20210803113134.2262882-5-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-5-iwona.winiarska@intel.com> Message-ID: On Tue, 03 Aug 2021 13:31:23 +0200, Iwona Winiarska wrote: > Add device tree bindings for the peci-aspeed controller driver. > > Co-developed-by: Jae Hyun Yoo > Signed-off-by: Jae Hyun Yoo > Signed-off-by: Iwona Winiarska > --- > .../devicetree/bindings/peci/peci-aspeed.yaml | 109 ++++++++++++++++++ > 1 file changed, 109 insertions(+) > create mode 100644 Documentation/devicetree/bindings/peci/peci-aspeed.yaml > Reviewed-by: Rob Herring From billy_tsai at aspeedtech.com Thu Aug 12 14:09:41 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Thu, 12 Aug 2021 12:09:41 +0800 Subject: [v11 1/2] dt-bindings: Add bindings for aspeed pwm-tach. In-Reply-To: <20210812040942.5365-1-billy_tsai@aspeedtech.com> References: <20210812040942.5365-1-billy_tsai@aspeedtech.com> Message-ID: <20210812040942.5365-2-billy_tsai@aspeedtech.com> This patch adds device binding for aspeed pwm-tach device which is a multi-function device include pwm and tach function and pwm/tach device bindings which should be the child-node of pwm-tach device. Signed-off-by: Billy Tsai --- .../bindings/hwmon/aspeed,ast2600-tach.yaml | 68 +++++++++++++++++ .../bindings/mfd/aspeed,ast2600-pwm-tach.yaml | 76 +++++++++++++++++++ .../bindings/pwm/aspeed,ast2600-pwm.yaml | 64 ++++++++++++++++ 3 files changed, 208 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/aspeed,ast2600-tach.yaml create mode 100644 Documentation/devicetree/bindings/mfd/aspeed,ast2600-pwm-tach.yaml create mode 100644 Documentation/devicetree/bindings/pwm/aspeed,ast2600-pwm.yaml diff --git a/Documentation/devicetree/bindings/hwmon/aspeed,ast2600-tach.yaml b/Documentation/devicetree/bindings/hwmon/aspeed,ast2600-tach.yaml new file mode 100644 index 000000000000..dded50a049fb --- /dev/null +++ b/Documentation/devicetree/bindings/hwmon/aspeed,ast2600-tach.yaml @@ -0,0 +1,68 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2021 Aspeed, Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwmon/aspeed,ast2600-tach.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Aspeed Ast2600 Tach controller + +maintainers: + - Billy Tsai + +description: | + The Aspeed Tach controller can support upto 16 fan input. + This module is part of the ast2600-pwm-tach multi-function device. For more + details see ../mfd/aspeed,ast2600-pwm-tach.yaml. + +properties: + compatible: + enum: + - aspeed,ast2600-tach + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + pinctrl-0: true + + pinctrl-names: + const: default + +required: + - compatible + - "#address-cells" + - "#size-cells" + +additionalProperties: + type: object + properties: + aspeed,tach-ch: + description: + The tach channel used for this node. + maxItems: 1 + + aspeed,min-rpm: + description: + define the minimal revolutions per minute of the measure fan + used to calculate the sample period of tach + default: 1000 + + aspeed,pulse-pr: + description: + Value specifying the number of pulses per revolution of the + monitored FAN. + default: 2 + + aspeed,tach-div: + description: + define the tachometer clock divider as an integer. Formula of + tach clock = clock source / (2^tach-div)^2 + minimum: 0 + maximum: 15 + default: 5 + + required: + - reg diff --git a/Documentation/devicetree/bindings/mfd/aspeed,ast2600-pwm-tach.yaml b/Documentation/devicetree/bindings/mfd/aspeed,ast2600-pwm-tach.yaml new file mode 100644 index 000000000000..1eaf6fab2752 --- /dev/null +++ b/Documentation/devicetree/bindings/mfd/aspeed,ast2600-pwm-tach.yaml @@ -0,0 +1,76 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2021 Aspeed, Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mfd/aspeed,ast2600-pwm-tach.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: PWM Tach controller Device Tree Bindings + +description: | + The PWM Tach controller is represented as a multi-function device which + includes: + PWM + Tach + +maintainers: + - Billy Tsai + +properties: + compatible: + items: + - enum: + - aspeed,ast2600-pwm-tach + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - resets + +patternProperties: + "^pwm(@[0-9a-f]+)?$": + $ref: ../pwm/aspeed,ast2600-pwm.yaml + + "^tach(@[0-9a-f]+)?$": + $ref: ../hwmon/aspeed,ast2600-tach.yaml + +additionalProperties: false + +examples: + - | + #include + pwm_tach: pwm_tach at 1e610000 { + compatible = "aspeed,ast2600-pwm-tach", "syscon", "simple-mfd"; + reg = <0x1e610000 0x100>; + clocks = <&syscon ASPEED_CLK_AHB>; + resets = <&syscon ASPEED_RESET_PWM>; + + pwm: pwm { + compatible = "aspeed,ast2600-pwm"; + #address-cells = <1>; + #size-cells = <0>; + #pwm-cells = <3>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_pwm0_default>; + }; + + tach: tach { + compatible = "aspeed,ast2600-tach"; + #address-cells = <1>; + #size-cells = <0>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_tach0_default>; + }; + }; diff --git a/Documentation/devicetree/bindings/pwm/aspeed,ast2600-pwm.yaml b/Documentation/devicetree/bindings/pwm/aspeed,ast2600-pwm.yaml new file mode 100644 index 000000000000..f501f8a769df --- /dev/null +++ b/Documentation/devicetree/bindings/pwm/aspeed,ast2600-pwm.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (C) 2021 Aspeed, Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/pwm/aspeed,ast2600-pwm.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Aspeed Ast2600 PWM controller + +maintainers: + - Billy Tsai + +description: | + The Aspeed PWM controller can support upto 16 PWM outputs. + This module is part of the ast2600-pwm-tach multi-function device. For more + details see ../mfd/aspeed,ast2600-pwm-tach.yaml. + +properties: + compatible: + enum: + - aspeed,ast2600-pwm + + "#pwm-cells": + const: 3 + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + + pinctrl-0: true + + pinctrl-names: + const: default + +required: + - compatible + - "#pwm-cells" + - "#address-cells" + - "#size-cells" + +additionalProperties: + description: Set extend properties for each pwm channel. + type: object + properties: + reg: + description: + The pwm channel index. + maxItems: 1 + + aspeed,wdt-reload-enable: + type: boolean + description: + Enable the function of wdt reset reload duty point. + + aspeed,wdt-reload-duty-point: + description: + Define the duty point after wdt reset, 0 = 100% + minimum: 0 + maximum: 255 + + required: + - reg -- 2.25.1 From billy_tsai at aspeedtech.com Thu Aug 12 14:09:40 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Thu, 12 Aug 2021 12:09:40 +0800 Subject: [v11 0/2] Support pwm driver for aspeed ast26xx Message-ID: <20210812040942.5365-1-billy_tsai@aspeedtech.com> The legacy driver of aspeed pwm is binding with tach controller and it doesn't follow the pwm framworks usage. In addition, the pwm register usage of the 6th generation of ast26xx has drastic change. So these patch serials add the new aspeed pwm driver to fix up the problem above. Change since v10: - pwm-aspeed-ast2600.c - Add more comment to explain the feature of PWM - Fix the naming of some parameters. - Set pin_enable and clk_enable at the same time. - Always set fixed divisor to hw register when apply. Change since v9: - dt-bindings: - Change the naming of tach subnode channel setting property to aspeed,tach-ch. - pwm-aspeed-ast2600.c - Fix the naming of some parameters. - Capitalise error messages. - Handling potentially mult overflow when .apply Change since v8: - pwm-aspeed-ast2600.c - Replace "* _BITULL(div_h)" to "<< div_h" - Fix duty_cycle precision problem. - Add the comment about the formula of duty_cycle. Change since v7: - pwm-aspeed-g6.c - Rename the driver: pwm-aspeed-g6.c -> pwm-aspeed-ast2600.c. - Macro remove "_CH" part of the register name. - Unroll the aspeed_pwm_get_period and remove it. - Simplify the formula to get duty_pt - Reduce the number of writing register. Organize all the fields and write them at once. Change since v6: - dt-bindings: - Add blank line between each DT property. - Change the sub-node name from fan to tach-ch. - pwm-aspeed-g6.c - Merge aspeed_pwm_set_period and aspeed_pwm_set_duty into .apply. - Convert the factor type to u64 when calculating the period value. - Using ROUND_UP strategy to calculate div_h for finer resolution. Change since v5: - pwm-aspeed-g6.c suggested by Uwe Kleine-K?nig - Move the divide at the end of the calculation. - Unified the prefix of the function name. - Use div64_u64 to calculate the divider of frequency. Change since v4: - dt_binding: - pwm/tach yaml: Replace child-node with additionalProperties - pwm-tach yaml: Replace child-node with patternProperties - pwm-aspeed-g6.c suggested by Uwe Kleine-K?nig - The bit definitions contained the name of the register. - Remove single caller function and fold it to the caller. - Avoid to divide by the result of a division. - Remove unnecessary condition in .apply(). - Use goto for error handling Changes since v3: - Add the dt_binding for aspeed,ast2600-tach. - Describe the pwm/tach as child-node of pwm-tach mfd. - Complete the properties of pwm node. Changes since v2: - Remove the tach node, #address-cells and #size-cells from pwm-tach.yaml - Add clocks and reset properties to pwm-tach.yaml - Kconfig/Makfile sorted alphabetically - pwm-aspeed-g6.c suggested by Uwe Kleine-K?nig - Add more hardware descriptions at top of the driver. - Remove unused api request and free - Move the initialize settings of all pwm channel to probe. - Change the method of getting the approximate period. - Read the hardware register values to fill the state for .get_state() Changes since v1: - Fix the dt_binding_check fail suggested by Rob Herring - Add depends to PWM_ASPEED_G6 configure suggested by Uwe Kleine-Konig - pwm-aspeed-g6.c suggested by Uwe Kleine-K?nig - Fix license header - Use bitfiled.h macro to define register fields - Implement .remove device function - Implement .get_state pwm api Billy Tsai (2): dt-bindings: Add bindings for aspeed pwm-tach. pwm: Add Aspeed ast2600 PWM support .../bindings/hwmon/aspeed,ast2600-tach.yaml | 68 ++++ .../bindings/mfd/aspeed,ast2600-pwm-tach.yaml | 76 ++++ .../bindings/pwm/aspeed,ast2600-pwm.yaml | 64 ++++ drivers/pwm/Kconfig | 10 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-aspeed-ast2600.c | 327 ++++++++++++++++++ 6 files changed, 546 insertions(+) create mode 100644 Documentation/devicetree/bindings/hwmon/aspeed,ast2600-tach.yaml create mode 100644 Documentation/devicetree/bindings/mfd/aspeed,ast2600-pwm-tach.yaml create mode 100644 Documentation/devicetree/bindings/pwm/aspeed,ast2600-pwm.yaml create mode 100644 drivers/pwm/pwm-aspeed-ast2600.c -- 2.25.1 From billy_tsai at aspeedtech.com Thu Aug 12 14:09:42 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Thu, 12 Aug 2021 12:09:42 +0800 Subject: [v11 2/2] pwm: Add Aspeed ast2600 PWM support In-Reply-To: <20210812040942.5365-1-billy_tsai@aspeedtech.com> References: <20210812040942.5365-1-billy_tsai@aspeedtech.com> Message-ID: <20210812040942.5365-3-billy_tsai@aspeedtech.com> This patch add the support of PWM controller which can be found at aspeed ast2600 soc. The pwm supoorts up to 16 channels and it's part function of multi-function device "pwm-tach controller". Signed-off-by: Billy Tsai --- drivers/pwm/Kconfig | 10 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-aspeed-ast2600.c | 327 +++++++++++++++++++++++++++++++ 3 files changed, 338 insertions(+) create mode 100644 drivers/pwm/pwm-aspeed-ast2600.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 63be5362fd3a..b0d26f6c2a8f 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -51,6 +51,16 @@ config PWM_AB8500 To compile this driver as a module, choose M here: the module will be called pwm-ab8500. +config PWM_ASPEED_AST2600 + tristate "Aspeed ast2600 PWM support" + depends on ARCH_ASPEED || COMPILE_TEST + depends on HAVE_CLK && HAS_IOMEM + help + This driver provides support for Aspeed ast2600 PWM controllers. + + To compile this driver as a module, choose M here: the module + will be called pwm-aspeed-ast2600. + config PWM_ATMEL tristate "Atmel PWM support" depends on OF diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index cbdcd55d69ee..ada454f9129a 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o +obj-$(CONFIG_PWM_ASPEED_AST2600) += pwm-aspeed-ast2600.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o diff --git a/drivers/pwm/pwm-aspeed-ast2600.c b/drivers/pwm/pwm-aspeed-ast2600.c new file mode 100644 index 000000000000..f89ce1d4cd67 --- /dev/null +++ b/drivers/pwm/pwm-aspeed-ast2600.c @@ -0,0 +1,327 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Aspeed Technology Inc. + * + * PWM controller driver for Aspeed ast2600 SoCs. + * This drivers doesn't support earlier version of the IP. + * + * The formula of pwm period duration: + * period duration = ((DIV_L + 1) * (PERIOD + 1) << DIV_H) / input-clk + * + * The formula of pwm duty cycle duration: + * duty cycle duration = period duration * DUTY_CYCLE_FALLING_POINT / (PERIOD + 1) + * = ((DIV_L + 1) * DUTY_CYCLE_FALLING_POINT << DIV_H) / input-clk + * + * The software driver fixes the period to 255, which causes the high-frequency + * precision of the PWM to be coarse, in exchange for the fineness of the duty cycle. + * + * Register usage: + * PIN_ENABLE: When it is unset the pwm controller will emit inactive level to the extern. + * Use to determine whether the PWM channel is enabled or disabled + * CLK_ENABLE: When it is unset the pwm controller will assert the duty counter reset and + * emit inactive level to the PIN_ENABLE mux after that the driver can still change the pwm period + * and duty and the value will apply when CLK_ENABLE be set again. + * Use to determine whether duty_cycle bigger than 0. + * PWM_ASPEED_CTRL_INVERSE: When it is toggled the output value will inverse immediately. + * PWM_ASPEED_DUTY_CYCLE_FALLING_POINT/PWM_ASPEED_DUTY_CYCLE_RISING_POINT: When these two + * values are equal it means the duty cycle = 100%. + * + * The glitch may generate at: + * - Enabled changing when the duty_cycle bigger than 0% and less than 100%. + * - Polarity changing when the duty_cycle bigger than 0% and less than 100%. + * - Set duty cycle to 0% from other values. + * + * Limitations: + * - When changing both duty cycle and period, we cannot prevent in + * software that the output might produce a period with mixed + * settings. + * - Disabling the PWM doesn't complete the current period. + * + * Improvements: + * - When only changing one of duty cycle or period, our pwm controller will not + * generate the glitch, the configure will change at next cycle of pwm. + * This improvement can disable/enable through PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The channel number of Aspeed pwm controller */ +#define PWM_ASPEED_NR_PWMS 16 + +/* PWM Control Register */ +#define PWM_ASPEED_CTRL(ch) ((ch) * 0x10 + 0x00) +#define PWM_ASPEED_CTRL_LOAD_SEL_RISING_AS_WDT BIT(19) +#define PWM_ASPEED_CTRL_DUTY_LOAD_AS_WDT_ENABLE BIT(18) +#define PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE BIT(17) +#define PWM_ASPEED_CTRL_CLK_ENABLE BIT(16) +#define PWM_ASPEED_CTRL_LEVEL_OUTPUT BIT(15) +#define PWM_ASPEED_CTRL_INVERSE BIT(14) +#define PWM_ASPEED_CTRL_OPEN_DRAIN_ENABLE BIT(13) +#define PWM_ASPEED_CTRL_PIN_ENABLE BIT(12) +#define PWM_ASPEED_CTRL_CLK_DIV_H GENMASK(11, 8) +#define PWM_ASPEED_CTRL_CLK_DIV_L GENMASK(7, 0) + +/* PWM Duty Cycle Register */ +#define PWM_ASPEED_DUTY_CYCLE(ch) ((ch) * 0x10 + 0x04) +#define PWM_ASPEED_DUTY_CYCLE_PERIOD GENMASK(31, 24) +#define PWM_ASPEED_DUTY_CYCLE_POINT_AS_WDT GENMASK(23, 16) +#define PWM_ASPEED_DUTY_CYCLE_FALLING_POINT GENMASK(15, 8) +#define PWM_ASPEED_DUTY_CYCLE_RISING_POINT GENMASK(7, 0) + +/* PWM fixed value */ +#define PWM_ASPEED_FIXED_PERIOD FIELD_MAX(PWM_ASPEED_DUTY_CYCLE_PERIOD) + +struct aspeed_pwm_data { + struct pwm_chip chip; + struct clk *clk; + struct regmap *regmap; + struct reset_control *reset; +}; + +static inline struct aspeed_pwm_data * +aspeed_pwm_chip_to_data(struct pwm_chip *chip) +{ + return container_of(chip, struct aspeed_pwm_data, chip); +} + +static void aspeed_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct device *dev = chip->dev; + struct aspeed_pwm_data *priv = aspeed_pwm_chip_to_data(chip); + u32 hwpwm = pwm->hwpwm; + bool polarity, pin_en, clk_en; + u32 duty_pt, val; + unsigned long rate; + u64 div_h, div_l, duty_cycle_period, dividend; + + regmap_read(priv->regmap, PWM_ASPEED_CTRL(hwpwm), &val); + polarity = FIELD_GET(PWM_ASPEED_CTRL_INVERSE, val); + pin_en = FIELD_GET(PWM_ASPEED_CTRL_PIN_ENABLE, val); + clk_en = FIELD_GET(PWM_ASPEED_CTRL_CLK_ENABLE, val); + div_h = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_H, val); + div_l = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_L, val); + regmap_read(priv->regmap, PWM_ASPEED_DUTY_CYCLE(hwpwm), &val); + duty_pt = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, val); + duty_cycle_period = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_PERIOD, val); + + rate = clk_get_rate(priv->clk); + + /* + * This multiplication doesn't overflow, the upper bound is + * 1000000000 * 256 * 256 << 15 = 0x1dcd650000000000 + */ + dividend = (u64)NSEC_PER_SEC * (div_l + 1) * (duty_cycle_period + 1) + << div_h; + state->period = DIV_ROUND_UP_ULL(dividend, rate); + + if (clk_en && duty_pt) { + dividend = (u64)NSEC_PER_SEC * (div_l + 1) * duty_pt + << div_h; + state->duty_cycle = DIV_ROUND_UP_ULL(dividend, rate); + } else + state->duty_cycle = clk_en ? state->period : 0; + state->polarity = polarity ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; + state->enabled = pin_en; + dev_dbg(dev, "get period: %lldns, duty_cycle: %lldns", state->period, + state->duty_cycle); +} + +static int aspeed_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct device *dev = chip->dev; + struct aspeed_pwm_data *priv = aspeed_pwm_chip_to_data(chip); + u32 hwpwm = pwm->hwpwm, duty_pt; + unsigned long rate; + u64 div_h, div_l, divisor; + bool clk_en; + + dev_dbg(dev, "expect period: %lldns, duty_cycle: %lldns", state->period, + state->duty_cycle); + + rate = clk_get_rate(priv->clk); + if (state->period > div64_u64(ULLONG_MAX, (u64)rate)) + return -ERANGE; + /* + * Pick the smallest value for div_h so that div_l can be the biggest + * which results in a finer resolution near the target period value. + */ + divisor = (u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1) * + (FIELD_MAX(PWM_ASPEED_CTRL_CLK_DIV_L) + 1); + div_h = order_base_2(DIV64_U64_ROUND_UP(rate * state->period, divisor)); + if (div_h > 0xf) + div_h = 0xf; + + divisor = ((u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1)) << div_h; + div_l = div64_u64(rate * state->period, divisor); + + if (div_l == 0) + return -ERANGE; + + div_l -= 1; + + if (div_l > 255) + div_l = 255; + + dev_dbg(dev, "clk source: %ld div_h %lld, div_l : %lld\n", rate, div_h, + div_l); + /* duty_pt = duty_cycle * (PERIOD + 1) / period */ + duty_pt = div64_u64(state->duty_cycle * rate, + (u64)NSEC_PER_SEC * (div_l + 1) << div_h); + dev_dbg(dev, "duty_cycle = %lld, duty_pt = %d\n", state->duty_cycle, + duty_pt); + + /* + * Fixed DUTY_CYCLE_PERIOD to its max value to get a + * fine-grained resolution for duty_cycle at the expense of a + * coarser period resolution. + */ + regmap_update_bits(priv->regmap, PWM_ASPEED_DUTY_CYCLE(hwpwm), + PWM_ASPEED_DUTY_CYCLE_PERIOD, + FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_PERIOD, + PWM_ASPEED_FIXED_PERIOD)); + if (duty_pt == 0) + /* emit inactive level and assert the duty counter reset */ + clk_en = 0; + else { + clk_en = 1; + if (duty_pt >= (PWM_ASPEED_FIXED_PERIOD + 1)) + duty_pt = 0; + regmap_update_bits( + priv->regmap, PWM_ASPEED_DUTY_CYCLE(hwpwm), + PWM_ASPEED_DUTY_CYCLE_RISING_POINT | + PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, + FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, + duty_pt)); + } + + regmap_update_bits( + priv->regmap, PWM_ASPEED_CTRL(hwpwm), + PWM_ASPEED_CTRL_CLK_DIV_H | PWM_ASPEED_CTRL_CLK_DIV_L | + PWM_ASPEED_CTRL_PIN_ENABLE | + PWM_ASPEED_CTRL_CLK_ENABLE | PWM_ASPEED_CTRL_INVERSE, + FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_H, div_h) | + FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_L, div_l) | + FIELD_PREP(PWM_ASPEED_CTRL_PIN_ENABLE, state->enabled) | + FIELD_PREP(PWM_ASPEED_CTRL_CLK_ENABLE, clk_en) | + FIELD_PREP(PWM_ASPEED_CTRL_INVERSE, state->polarity)); + return 0; +} + +static const struct pwm_ops aspeed_pwm_ops = { + .apply = aspeed_pwm_apply, + .get_state = aspeed_pwm_get_state, + .owner = THIS_MODULE, +}; + +static int aspeed_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + struct aspeed_pwm_data *priv; + struct device_node *np; + struct platform_device *parent_dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + np = pdev->dev.parent->of_node; + if (!of_device_is_compatible(np, "aspeed,ast2600-pwm-tach")) + return dev_err_probe(dev, -ENODEV, + "Unsupported pwm device binding\n"); + + priv->regmap = syscon_node_to_regmap(np); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "Couldn't get regmap\n"); + + parent_dev = of_find_device_by_node(np); + priv->clk = devm_clk_get(&parent_dev->dev, 0); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Couldn't get clock\n"); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return dev_err_probe(dev, ret, "Couldn't enable clock\n"); + + priv->reset = devm_reset_control_get_shared(&parent_dev->dev, NULL); + if (IS_ERR(priv->reset)) { + ret = dev_err_probe(dev, PTR_ERR(priv->reset), + "Get reset failed\n"); + goto err_disable_clk; + } + ret = reset_control_deassert(priv->reset); + if (ret) { + dev_err_probe(dev, ret, "Couldn't deassert reset control\n"); + goto err_disable_clk; + } + + priv->chip.dev = dev; + priv->chip.ops = &aspeed_pwm_ops; + priv->chip.npwm = PWM_ASPEED_NR_PWMS; + + ret = pwmchip_add(&priv->chip); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + goto err_assert_reset; + } + dev_set_drvdata(dev, priv); + return 0; +err_assert_reset: + reset_control_assert(priv->reset); +err_disable_clk: + clk_disable_unprepare(priv->clk); + return ret; +} + +static int aspeed_pwm_remove(struct platform_device *dev) +{ + struct aspeed_pwm_data *priv = platform_get_drvdata(dev); + + pwmchip_remove(&priv->chip); + reset_control_assert(priv->reset); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct of_device_id of_pwm_match_table[] = { + { + .compatible = "aspeed,ast2600-pwm", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_pwm_match_table); + +static struct platform_driver aspeed_pwm_driver = { + .probe = aspeed_pwm_probe, + .remove = aspeed_pwm_remove, + .driver = { + .name = "aspeed-pwm", + .of_match_table = of_pwm_match_table, + }, +}; + +module_platform_driver(aspeed_pwm_driver); + +MODULE_AUTHOR("Billy Tsai "); +MODULE_DESCRIPTION("Aspeed ast2600 PWM device driver"); +MODULE_LICENSE("GPL v2"); -- 2.25.1 From billy_tsai at aspeedtech.com Thu Aug 12 14:14:23 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Thu, 12 Aug 2021 04:14:23 +0000 Subject: [v10 2/2] pwm: Add Aspeed ast2600 PWM support In-Reply-To: <20210716031412.19258-3-billy_tsai@aspeedtech.com> References: <20210716031412.19258-1-billy_tsai@aspeedtech.com> <20210716031412.19258-3-billy_tsai@aspeedtech.com> Message-ID: <08A89C49-0625-49DF-A0FC-1AC6AA6307D4@aspeedtech.com> I have sent the v11 patch. Suspend this one. Best Regards, Billy Tsai ?On 2021/7/16, 11:14 AM, "Billy Tsai" wrote: This patch add the support of PWM controller which can be found at aspeed ast2600 soc. The pwm supoorts up to 16 channels and it's part function of multi-function device "pwm-tach controller". Signed-off-by: Billy Tsai --- drivers/pwm/Kconfig | 10 + drivers/pwm/Makefile | 1 + drivers/pwm/pwm-aspeed-ast2600.c | 322 +++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+) create mode 100644 drivers/pwm/pwm-aspeed-ast2600.c diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig index 63be5362fd3a..b0d26f6c2a8f 100644 --- a/drivers/pwm/Kconfig +++ b/drivers/pwm/Kconfig @@ -51,6 +51,16 @@ config PWM_AB8500 To compile this driver as a module, choose M here: the module will be called pwm-ab8500. +config PWM_ASPEED_AST2600 + tristate "Aspeed ast2600 PWM support" + depends on ARCH_ASPEED || COMPILE_TEST + depends on HAVE_CLK && HAS_IOMEM + help + This driver provides support for Aspeed ast2600 PWM controllers. + + To compile this driver as a module, choose M here: the module + will be called pwm-aspeed-ast2600. + config PWM_ATMEL tristate "Atmel PWM support" depends on OF diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile index cbdcd55d69ee..ada454f9129a 100644 --- a/drivers/pwm/Makefile +++ b/drivers/pwm/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_PWM) += core.o obj-$(CONFIG_PWM_SYSFS) += sysfs.o obj-$(CONFIG_PWM_AB8500) += pwm-ab8500.o +obj-$(CONFIG_PWM_ASPEED_AST2600) += pwm-aspeed-ast2600.o obj-$(CONFIG_PWM_ATMEL) += pwm-atmel.o obj-$(CONFIG_PWM_ATMEL_HLCDC_PWM) += pwm-atmel-hlcdc.o obj-$(CONFIG_PWM_ATMEL_TCB) += pwm-atmel-tcb.o diff --git a/drivers/pwm/pwm-aspeed-ast2600.c b/drivers/pwm/pwm-aspeed-ast2600.c new file mode 100644 index 000000000000..8758ce7432eb --- /dev/null +++ b/drivers/pwm/pwm-aspeed-ast2600.c @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Aspeed Technology Inc. + * + * PWM controller driver for Aspeed ast2600 SoCs. + * This drivers doesn't support earlier version of the IP. + * + * The formula of pwm period duration: + * period duration = ((DIV_L + 1) * (PERIOD + 1) << DIV_H) / input-clk + * + * The formula of pwm duty cycle duration: + * duty cycle duration = period duration * DUTY_CYCLE_FALLING_POINT / (PERIOD + 1) + * = ((DIV_L + 1) * DUTY_CYCLE_FALLING_POINT << DIV_H) / input-clk + * + * The software driver fixes the period to 255, which causes the high-frequency + * precision of the PWM to be coarse, in exchange for the fineness of the duty cycle. + * + * Register usage: + * PIN_ENABLE: When it is unset the pwm controller will always output low to the extern. + * Use to determine whether the PWM channel is enabled or disabled + * CLK_ENABLE: When it is unset the pwm controller will reset the duty counter to 0 and + * output low to the PIN_ENABLE mux after that the driver can still change the pwm period + * and duty and the value will apply when CLK_ENABLE be set again. + * Use to determine whether duty_cycle bigger than 0. + * PWM_ASPEED_CTRL_INVERSE: When it is toggled the output value will inverse immediately. + * PWM_ASPEED_DUTY_CYCLE_FALLING_POINT/PWM_ASPEED_DUTY_CYCLE_RISING_POINT: When these two + * values are equal it means the duty cycle = 100%. + * + * Limitations: + * - When changing both duty cycle and period, we cannot prevent in + * software that the output might produce a period with mixed + * settings. + * - Disabling the PWM doesn't complete the current period. + * + * Improvements: + * - When only changing one of duty cycle or period, our pwm controller will not + * generate the glitch, the configure will change at next cycle of pwm. + * This improvement can disable/enable through PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* The channel number of Aspeed pwm controller */ +#define PWM_ASPEED_NR_PWMS 16 + +/* PWM Control Register */ +#define PWM_ASPEED_CTRL(ch) ((ch) * 0x10 + 0x00) +#define PWM_ASPEED_CTRL_LOAD_SEL_RISING_AS_WDT BIT(19) +#define PWM_ASPEED_CTRL_DUTY_LOAD_AS_WDT_ENABLE BIT(18) +#define PWM_ASPEED_CTRL_DUTY_SYNC_DISABLE BIT(17) +#define PWM_ASPEED_CTRL_CLK_ENABLE BIT(16) +#define PWM_ASPEED_CTRL_LEVEL_OUTPUT BIT(15) +#define PWM_ASPEED_CTRL_INVERSE BIT(14) +#define PWM_ASPEED_CTRL_OPEN_DRAIN_ENABLE BIT(13) +#define PWM_ASPEED_CTRL_PIN_ENABLE BIT(12) +#define PWM_ASPEED_CTRL_CLK_DIV_H GENMASK(11, 8) +#define PWM_ASPEED_CTRL_CLK_DIV_L GENMASK(7, 0) + +/* PWM Duty Cycle Register */ +#define PWM_ASPEED_DUTY_CYCLE(ch) ((ch) * 0x10 + 0x04) +#define PWM_ASPEED_DUTY_CYCLE_PERIOD GENMASK(31, 24) +#define PWM_ASPEED_DUTY_CYCLE_POINT_AS_WDT GENMASK(23, 16) +#define PWM_ASPEED_DUTY_CYCLE_FALLING_POINT GENMASK(15, 8) +#define PWM_ASPEED_DUTY_CYCLE_RISING_POINT GENMASK(7, 0) + +/* PWM fixed value */ +#define PWM_ASPEED_FIXED_PERIOD FIELD_MAX(PWM_ASPEED_DUTY_CYCLE_PERIOD) + +struct aspeed_pwm_data { + struct pwm_chip chip; + struct clk *clk; + struct regmap *regmap; + struct reset_control *reset; +}; + +static inline struct aspeed_pwm_data * +aspeed_pwm_chip_to_data(struct pwm_chip *chip) +{ + return container_of(chip, struct aspeed_pwm_data, chip); +} + +static void aspeed_pwm_get_state(struct pwm_chip *chip, struct pwm_device *pwm, + struct pwm_state *state) +{ + struct device *dev = chip->dev; + struct aspeed_pwm_data *priv = aspeed_pwm_chip_to_data(chip); + u32 index = pwm->hwpwm; + bool polarity, pin_en, clk_en; + u32 duty_pt, val; + unsigned long rate; + u64 div_h, div_l, duty_cycle_period, dividend; + + regmap_read(priv->regmap, PWM_ASPEED_CTRL(index), &val); + polarity = FIELD_GET(PWM_ASPEED_CTRL_INVERSE, val); + pin_en = FIELD_GET(PWM_ASPEED_CTRL_PIN_ENABLE, val); + clk_en = FIELD_GET(PWM_ASPEED_CTRL_CLK_ENABLE, val); + div_h = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_H, val); + div_l = FIELD_GET(PWM_ASPEED_CTRL_CLK_DIV_L, val); + regmap_read(priv->regmap, PWM_ASPEED_DUTY_CYCLE(index), &val); + duty_pt = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, val); + duty_cycle_period = FIELD_GET(PWM_ASPEED_DUTY_CYCLE_PERIOD, val); + + rate = clk_get_rate(priv->clk); + + /* + * This multiplication doesn't overflow, the upper bound is + * 1000000000 * 256 * 256 << 15 = 0x1dcd650000000000 + */ + dividend = (u64)NSEC_PER_SEC * (div_l + 1) * (duty_cycle_period + 1) + << div_h; + state->period = DIV_ROUND_UP_ULL(dividend, rate); + + if (clk_en && duty_pt) { + dividend = (u64)NSEC_PER_SEC * (div_l + 1) * duty_pt + << div_h; + state->duty_cycle = DIV_ROUND_UP_ULL(dividend, rate); + } else + state->duty_cycle = clk_en ? state->period : 0; + state->polarity = polarity ? PWM_POLARITY_INVERSED : PWM_POLARITY_NORMAL; + state->enabled = pin_en; + dev_dbg(dev, "get period: %lldns, duty_cycle: %lldns", state->period, + state->duty_cycle); +} + +static int aspeed_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm, + const struct pwm_state *state) +{ + struct device *dev = chip->dev; + struct aspeed_pwm_data *priv = aspeed_pwm_chip_to_data(chip); + u32 hwpwm = pwm->hwpwm, duty_pt; + unsigned long rate; + u64 div_h, div_l, divisor; + bool clk_en; + + dev_dbg(dev, "expect period: %lldns, duty_cycle: %lldns", state->period, + state->duty_cycle); + + rate = clk_get_rate(priv->clk); + if (state->period > div64_u64(ULLONG_MAX, (u64)rate)) + return -ERANGE; + /* + * Pick the smallest value for div_h so that div_l can be the biggest + * which results in a finer resolution near the target period value. + */ + divisor = (u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1) * + (FIELD_MAX(PWM_ASPEED_CTRL_CLK_DIV_L) + 1); + div_h = order_base_2(DIV64_U64_ROUND_UP(rate * state->period, divisor)); + if (div_h > 0xf) + div_h = 0xf; + + divisor = ((u64)NSEC_PER_SEC * (PWM_ASPEED_FIXED_PERIOD + 1)) << div_h; + div_l = div64_u64(rate * state->period, divisor); + + if (div_l == 0) + return -ERANGE; + + div_l -= 1; + + if (div_l > 255) + div_l = 255; + + dev_dbg(dev, "clk source: %ld div_h %lld, div_l : %lld\n", rate, div_h, + div_l); + /* duty_pt = duty_cycle * (PERIOD + 1) / period */ + duty_pt = div64_u64(state->duty_cycle * rate, + (u64)NSEC_PER_SEC * (div_l + 1) << div_h); + dev_dbg(dev, "duty_cycle = %lld, duty_pt = %d\n", state->duty_cycle, + duty_pt); + + regmap_update_bits(priv->regmap, PWM_ASPEED_CTRL(hwpwm), + PWM_ASPEED_CTRL_PIN_ENABLE, + state->enabled ? PWM_ASPEED_CTRL_PIN_ENABLE : 0); + + if (duty_pt == 0) + clk_en = 0; + else { + clk_en = 1; + if (duty_pt >= (PWM_ASPEED_FIXED_PERIOD + 1)) + duty_pt = 0; + /* + * Fixed DUTY_CYCLE_PERIOD to its max value to get a + * fine-grained resolution for duty_cycle at the expense of a + * coarser period resolution. + */ + regmap_update_bits( + priv->regmap, PWM_ASPEED_DUTY_CYCLE(hwpwm), + PWM_ASPEED_DUTY_CYCLE_PERIOD | + PWM_ASPEED_DUTY_CYCLE_RISING_POINT | + PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, + FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_PERIOD, + PWM_ASPEED_FIXED_PERIOD) | + FIELD_PREP(PWM_ASPEED_DUTY_CYCLE_FALLING_POINT, + duty_pt)); + } + + regmap_update_bits( + priv->regmap, PWM_ASPEED_CTRL(hwpwm), + PWM_ASPEED_CTRL_CLK_DIV_H | PWM_ASPEED_CTRL_CLK_DIV_L | + PWM_ASPEED_CTRL_CLK_ENABLE | PWM_ASPEED_CTRL_INVERSE, + FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_H, div_h) | + FIELD_PREP(PWM_ASPEED_CTRL_CLK_DIV_L, div_l) | + FIELD_PREP(PWM_ASPEED_CTRL_CLK_ENABLE, clk_en) | + FIELD_PREP(PWM_ASPEED_CTRL_INVERSE, state->polarity)); + return 0; +} + +static const struct pwm_ops aspeed_pwm_ops = { + .apply = aspeed_pwm_apply, + .get_state = aspeed_pwm_get_state, + .owner = THIS_MODULE, +}; + +static int aspeed_pwm_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int ret; + struct aspeed_pwm_data *priv; + struct device_node *np; + struct platform_device *parent_dev; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + np = pdev->dev.parent->of_node; + if (!of_device_is_compatible(np, "aspeed,ast2600-pwm-tach")) + return dev_err_probe(dev, -ENODEV, + "Unsupported pwm device binding\n"); + + priv->regmap = syscon_node_to_regmap(np); + if (IS_ERR(priv->regmap)) + return dev_err_probe(dev, PTR_ERR(priv->regmap), + "Couldn't get regmap\n"); + + parent_dev = of_find_device_by_node(np); + priv->clk = devm_clk_get(&parent_dev->dev, 0); + if (IS_ERR(priv->clk)) + return dev_err_probe(dev, PTR_ERR(priv->clk), + "Couldn't get clock\n"); + + ret = clk_prepare_enable(priv->clk); + if (ret) + return dev_err_probe(dev, ret, "Couldn't enable clock\n"); + + priv->reset = devm_reset_control_get_shared(&parent_dev->dev, NULL); + if (IS_ERR(priv->reset)) { + ret = dev_err_probe(dev, PTR_ERR(priv->reset), + "Get reset failed\n"); + goto err_disable_clk; + } + ret = reset_control_deassert(priv->reset); + if (ret) { + dev_err_probe(dev, ret, "Couldn't deassert reset control\n"); + goto err_disable_clk; + } + + priv->chip.dev = dev; + priv->chip.ops = &aspeed_pwm_ops; + priv->chip.npwm = PWM_ASPEED_NR_PWMS; + + ret = pwmchip_add(&priv->chip); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to add PWM chip\n"); + goto err_assert_reset; + } + dev_set_drvdata(dev, priv); + return 0; +err_assert_reset: + reset_control_assert(priv->reset); +err_disable_clk: + clk_disable_unprepare(priv->clk); + return ret; +} + +static int aspeed_pwm_remove(struct platform_device *dev) +{ + struct aspeed_pwm_data *priv = platform_get_drvdata(dev); + + pwmchip_remove(&priv->chip); + reset_control_assert(priv->reset); + clk_disable_unprepare(priv->clk); + + return 0; +} + +static const struct of_device_id of_pwm_match_table[] = { + { + .compatible = "aspeed,ast2600-pwm", + }, + {}, +}; +MODULE_DEVICE_TABLE(of, of_pwm_match_table); + +static struct platform_driver aspeed_pwm_driver = { + .probe = aspeed_pwm_probe, + .remove = aspeed_pwm_remove, + .driver = { + .name = "aspeed-pwm", + .of_match_table = of_pwm_match_table, + }, +}; + +module_platform_driver(aspeed_pwm_driver); + +MODULE_AUTHOR("Billy Tsai "); +MODULE_DESCRIPTION("Aspeed ast2600 PWM device driver"); +MODULE_LICENSE("GPL v2"); -- 2.25.1 From wsa at kernel.org Thu Aug 12 17:39:43 2021 From: wsa at kernel.org (Wolfram Sang) Date: Thu, 12 Aug 2021 09:39:43 +0200 Subject: [PATCH v5 1/3] i2c: aspeed: Add slave_enable() to toggle slave mode In-Reply-To: <20210714033833.11640-2-quan@os.amperecomputing.com> References: <20210714033833.11640-1-quan@os.amperecomputing.com> <20210714033833.11640-2-quan@os.amperecomputing.com> Message-ID: Hi all, On Wed, Jul 14, 2021 at 10:38:31AM +0700, Quan Nguyen wrote: > Slave needs time to prepare the response data before Master could > enquiry via read transaction. However, there is no mechanism for > i2c-aspeed Slave to notify Master that it needs more time to process > and this make Master side to time out when trying to get the response. > > This commit introduces the slave_enable() callback in struct > i2c_algorithm for Slave to temporary stop the Slave mode while working > on the response and re-enable the Slave when response data ready. Sorry that I couldn't chime in earlier, but NAK! > include/linux/i2c.h | 2 ++ @Corey: Please do not change this file without my ACK. It is not a trivial change but an API extenstion and that should really be acked by the subsystem maintainer, in this case me. I was really surprised to see this in linux-next already. @all: Plus, I neither like the API (because it doesn't look generic to me but mostly handling one issue needed here) nor do I fully understand the use case. Normally, when a read is requested and the backend needs time to deliver the data, the hardware should stretch the SCL clock until some data register is finally written to. If it doesn't do it for whatever reason, this is a quirky hardware in my book and needs handling in the driver only. So, what is special with this HW? Can't we solve it differently? All the best, Wolfram -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 833 bytes Desc: not available URL: From minyard at acm.org Thu Aug 12 23:36:55 2021 From: minyard at acm.org (Corey Minyard) Date: Thu, 12 Aug 2021 08:36:55 -0500 Subject: [Openipmi-developer] [PATCH v5 1/3] i2c: aspeed: Add slave_enable() to toggle slave mode In-Reply-To: References: <20210714033833.11640-1-quan@os.amperecomputing.com> <20210714033833.11640-2-quan@os.amperecomputing.com> Message-ID: <20210812133655.GT3406@minyard.net> On Thu, Aug 12, 2021 at 09:39:43AM +0200, Wolfram Sang wrote: > Hi all, > > On Wed, Jul 14, 2021 at 10:38:31AM +0700, Quan Nguyen wrote: > > Slave needs time to prepare the response data before Master could > > enquiry via read transaction. However, there is no mechanism for > > i2c-aspeed Slave to notify Master that it needs more time to process > > and this make Master side to time out when trying to get the response. > > > > This commit introduces the slave_enable() callback in struct > > i2c_algorithm for Slave to temporary stop the Slave mode while working > > on the response and re-enable the Slave when response data ready. > > Sorry that I couldn't chime in earlier, but NAK! > > > include/linux/i2c.h | 2 ++ > > @Corey: Please do not change this file without my ACK. It is not a > trivial change but an API extenstion and that should really be acked by > the subsystem maintainer, in this case me. I was really surprised to see > this in linux-next already. I am sorry, I'll pull it out. -corey > > @all: Plus, I neither like the API (because it doesn't look generic to > me but mostly handling one issue needed here) nor do I fully understand > the use case. Normally, when a read is requested and the backend needs > time to deliver the data, the hardware should stretch the SCL clock > until some data register is finally written to. If it doesn't do it for > whatever reason, this is a quirky hardware in my book and needs handling > in the driver only. So, what is special with this HW? Can't we solve it > differently? > > All the best, > > Wolfram > > _______________________________________________ > Openipmi-developer mailing list > Openipmi-developer at lists.sourceforge.net > https://lists.sourceforge.net/lists/listinfo/openipmi-developer From joel at jms.id.au Fri Aug 13 11:29:17 2021 From: joel at jms.id.au (Joel Stanley) Date: Fri, 13 Aug 2021 01:29:17 +0000 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: <20210805222818.8391-5-rentao.bupt@gmail.com> References: <20210805222818.8391-1-rentao.bupt@gmail.com> <20210805222818.8391-5-rentao.bupt@gmail.com> Message-ID: On Thu, 5 Aug 2021 at 22:28, wrote: > +&mdio1 { > + status = "okay"; You're enabling this but it looks like it's unused? > +}; > + > +&mdio3 { > + status = "okay"; > + > + ethphy1: ethernet-phy at 13 { > + compatible = "ethernet-phy-ieee802.3-c22"; > + reg = <0x0d>; > + }; > +}; > + > +&mac3 { > + status = "okay"; > + phy-mode = "rgmii"; > + phy-handle = <ðphy1>; > + pinctrl-names = "default"; > + pinctrl-0 = <&pinctrl_rgmii4_default>; > +}; From rentao.bupt at gmail.com Fri Aug 13 13:40:17 2021 From: rentao.bupt at gmail.com (Tao Ren) Date: Thu, 12 Aug 2021 20:40:17 -0700 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: References: <20210805222818.8391-1-rentao.bupt@gmail.com> <20210805222818.8391-5-rentao.bupt@gmail.com> Message-ID: <20210813034016.GA21895@taoren-ubuntu-R90MNF91> On Fri, Aug 13, 2021 at 01:29:17AM +0000, Joel Stanley wrote: > On Thu, 5 Aug 2021 at 22:28, wrote: > > +&mdio1 { > > + status = "okay"; > > You're enabling this but it looks like it's unused? Thanks Joel for the careful review. The MDIO controller is not paired with BMC MAC; instead, it's connected to the MDC/MDIO interface of the on-board switch (whose ports are connected to BMC, Host and front panel management port). In other word, the MDIO controller is used, but we do need some user space scripts to control the controller. What's your suggestion? For example, should I add some comments to explain the purpose? Cheers, Tao > > > +}; > > + > > +&mdio3 { > > + status = "okay"; > > + > > + ethphy1: ethernet-phy at 13 { > > + compatible = "ethernet-phy-ieee802.3-c22"; > > + reg = <0x0d>; > > + }; > > +}; > > + > > +&mac3 { > > + status = "okay"; > > + phy-mode = "rgmii"; > > + phy-handle = <ðphy1>; > > + pinctrl-names = "default"; > > + pinctrl-0 = <&pinctrl_rgmii4_default>; > > +}; From joel at jms.id.au Fri Aug 13 14:13:23 2021 From: joel at jms.id.au (Joel Stanley) Date: Fri, 13 Aug 2021 04:13:23 +0000 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: <20210813034016.GA21895@taoren-ubuntu-R90MNF91> References: <20210805222818.8391-1-rentao.bupt@gmail.com> <20210805222818.8391-5-rentao.bupt@gmail.com> <20210813034016.GA21895@taoren-ubuntu-R90MNF91> Message-ID: On Fri, 13 Aug 2021 at 03:40, Tao Ren wrote: > > On Fri, Aug 13, 2021 at 01:29:17AM +0000, Joel Stanley wrote: > > On Thu, 5 Aug 2021 at 22:28, wrote: > > > +&mdio1 { > > > + status = "okay"; > > > > You're enabling this but it looks like it's unused? > > Thanks Joel for the careful review. The MDIO controller is not paired > with BMC MAC; instead, it's connected to the MDC/MDIO interface of the > on-board switch (whose ports are connected to BMC, Host and front panel > management port). I see! > > In other word, the MDIO controller is used, but we do need some user > space scripts to control the controller. What's your suggestion? For > example, should I add some comments to explain the purpose? Yes, that would make sense. I've applied your patch, so if you want to add comments please send a diff against this tree: https://git.kernel.org/pub/scm/linux/kernel/git/joel/bmc.git/log/?h=dt-for-v5.15 From chiawei_wang at aspeedtech.com Fri Aug 13 15:47:56 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Fri, 13 Aug 2021 13:47:56 +0800 Subject: [PATCH 0/2] aspeed: Add LPC mailbox support Message-ID: <20210813054758.13189-1-chiawei_wang@aspeedtech.com> Add driver support for the LPC mailbox controller of ASPEED SoCs. Chia-Wei Wang (2): soc: aspeed: Add LPC mailbox support ARM: dts: aspeed: Add mailbox to device tree arch/arm/boot/dts/aspeed-g4.dtsi | 7 + arch/arm/boot/dts/aspeed-g5.dtsi | 8 +- arch/arm/boot/dts/aspeed-g6.dtsi | 7 + drivers/soc/aspeed/Kconfig | 10 + drivers/soc/aspeed/Makefile | 9 +- drivers/soc/aspeed/aspeed-lpc-mbox.c | 415 +++++++++++++++++++++++++++ 6 files changed, 451 insertions(+), 5 deletions(-) create mode 100644 drivers/soc/aspeed/aspeed-lpc-mbox.c -- 2.17.1 From chiawei_wang at aspeedtech.com Fri Aug 13 15:47:57 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Fri, 13 Aug 2021 13:47:57 +0800 Subject: [PATCH 1/2] soc: aspeed: Add LPC mailbox support In-Reply-To: <20210813054758.13189-1-chiawei_wang@aspeedtech.com> References: <20210813054758.13189-1-chiawei_wang@aspeedtech.com> Message-ID: <20210813054758.13189-2-chiawei_wang@aspeedtech.com> The LPC mailbox controller consists of multiple general registers for the communication between the Host and the BMC. The interrupts for data update signaling are also introduced. Signed-off-by: Chia-Wei Wang --- drivers/soc/aspeed/Kconfig | 10 + drivers/soc/aspeed/Makefile | 9 +- drivers/soc/aspeed/aspeed-lpc-mbox.c | 415 +++++++++++++++++++++++++++ 3 files changed, 430 insertions(+), 4 deletions(-) create mode 100644 drivers/soc/aspeed/aspeed-lpc-mbox.c diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index 243ca196e6ad..5b31a6e2620c 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -24,6 +24,16 @@ config ASPEED_LPC_SNOOP allows the BMC to listen on and save the data written by the host to an arbitrary LPC I/O port. +config ASPEED_LPC_MAILBOX + tristate "ASPEED LPC mailbox support" + select REGMAP + select MFD_SYSCON + default ARCH_ASPEED + help + Provides a driver to control the LPC mailbox which possesses + up to 32 data registers for the communication between the Host + and the BMC over LPC. + config ASPEED_P2A_CTRL tristate "ASPEED P2A (VGA MMIO to BMC) bridge control" select REGMAP diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index fcab7192e1a4..cde6b4514c97 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o -obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o -obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o -obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o +obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o +obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o +obj-$(CONFIG_ASPEED_LPC_MAILBOX) += aspeed-lpc-mbox.o +obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o +obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c new file mode 100644 index 000000000000..ac8f4c5fbbb1 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2017 IBM Corporation + * Copyright 2021 Aspeed Technology Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "aspeed-mbox" + +#define ASPEED_MBOX_DR(dr, n) (dr + (n * 4)) +#define ASPEED_MBOX_STR(str, n) (str + (n / 8) * 4) +#define ASPEED_MBOX_BIE(bie, n) (bie + (n / 8) * 4) +#define ASPEED_MBOX_HIE(hie, n) (hie + (n / 8) * 4) + +#define ASPEED_MBOX_BCR_RECV BIT(7) +#define ASPEED_MBOX_BCR_MASK BIT(1) +#define ASPEED_MBOX_BCR_SEND BIT(0) + +/* ioctl code */ +#define ASPEED_MBOX_IOCTL 0xA3 +#define ASPEED_MBOX_IOCTL_GET_SIZE \ + _IOR(ASPEED_MBOX_IOCTL, 0, struct aspeed_mbox_ioctl_data) + +struct aspeed_mbox_ioctl_data { + unsigned int data; +}; + +struct aspeed_mbox_model { + unsigned int dr_num; + + /* offsets to the MBOX registers */ + unsigned int dr; + unsigned int str; + unsigned int bcr; + unsigned int hcr; + unsigned int bie; + unsigned int hie; +}; + +struct aspeed_mbox { + struct miscdevice miscdev; + struct regmap *map; + unsigned int base; + wait_queue_head_t queue; + struct mutex mutex; + const struct aspeed_mbox_model *model; +}; + +static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0); + +static u8 aspeed_mbox_inb(struct aspeed_mbox *mbox, int reg) +{ + /* + * The mbox registers are actually only one byte but are addressed + * four bytes apart. The other three bytes are marked 'reserved', + * they *should* be zero but lets not rely on it. + * I am going to rely on the fact we can casually read/write to them... + */ + unsigned int val = 0xff; /* If regmap throws an error return 0xff */ + int rc = regmap_read(mbox->map, mbox->base + reg, &val); + + if (rc) + dev_err(mbox->miscdev.parent, "regmap_read() failed with " + "%d (reg: 0x%08x)\n", rc, reg); + + return val & 0xff; +} + +static void aspeed_mbox_outb(struct aspeed_mbox *mbox, u8 data, int reg) +{ + int rc = regmap_write(mbox->map, mbox->base + reg, data); + + if (rc) + dev_err(mbox->miscdev.parent, "regmap_write() failed with " + "%d (data: %u reg: 0x%08x)\n", rc, data, reg); +} + +static struct aspeed_mbox *file_mbox(struct file *file) +{ + return container_of(file->private_data, struct aspeed_mbox, miscdev); +} + +static int aspeed_mbox_open(struct inode *inode, struct file *file) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + + if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { + /* + * Clear the interrupt status bit if it was left on and unmask + * interrupts. + * ASPEED_MBOX_BCR_RECV bit is W1C, this also unmasks in 1 step + */ + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); + return 0; + } + + atomic_dec(&aspeed_mbox_open_count); + return -EBUSY; +} + +static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + char __user *p = buf; + ssize_t ret; + int i; + + if (!access_ok(buf, count)) + return -EFAULT; + + if (count + *ppos > model->dr_num) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) { + if (!(aspeed_mbox_inb(mbox, model->bcr) & + ASPEED_MBOX_BCR_RECV)) + return -EAGAIN; + } else if (wait_event_interruptible(mbox->queue, + aspeed_mbox_inb(mbox, model->bcr) & + ASPEED_MBOX_BCR_RECV)) { + return -ERESTARTSYS; + } + + mutex_lock(&mbox->mutex); + + for (i = *ppos; count > 0 && i < model->dr_num; i++) { + uint8_t reg = aspeed_mbox_inb(mbox, ASPEED_MBOX_DR(model->dr, i)); + + ret = __put_user(reg, p); + if (ret) + goto out_unlock; + + p++; + count--; + } + + /* ASPEED_MBOX_BCR_RECV bit is write to clear, this also unmasks in 1 step */ + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); + ret = p - buf; + +out_unlock: + mutex_unlock(&mbox->mutex); + return ret; +} + +static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + const char __user *p = buf; + ssize_t ret; + char c; + int i; + + if (!access_ok(buf, count)) + return -EFAULT; + + if (count + *ppos > model->dr_num) + return -EINVAL; + + mutex_lock(&mbox->mutex); + + for (i = *ppos; count > 0 && i < model->dr_num; i++) { + ret = __get_user(c, p); + if (ret) + goto out_unlock; + + aspeed_mbox_outb(mbox, c, ASPEED_MBOX_DR(model->dr, i)); + p++; + count--; + } + + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_SEND, model->bcr); + ret = p - buf; + +out_unlock: + mutex_unlock(&mbox->mutex); + return ret; +} + +static unsigned int aspeed_mbox_poll(struct file *file, poll_table *wait) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + unsigned int mask = 0; + + poll_wait(file, &mbox->queue, wait); + + if (aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV) + mask |= POLLIN; + + return mask; +} + +static int aspeed_mbox_release(struct inode *inode, struct file *file) +{ + atomic_dec(&aspeed_mbox_open_count); + return 0; +} + +static long aspeed_mbox_ioctl(struct file *file, unsigned int cmd, + unsigned long param) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + struct aspeed_mbox_ioctl_data data; + long ret; + + switch (cmd) { + case ASPEED_MBOX_IOCTL_GET_SIZE: + data.data = model->dr_num; + ret = copy_to_user((void __user *)param, &data, sizeof(data)); + break; + default: + ret = -ENOTTY; + } + return ret; +} + +static const struct file_operations aspeed_mbox_fops = { + .owner = THIS_MODULE, + .llseek = no_seek_end_llseek, + .read = aspeed_mbox_read, + .write = aspeed_mbox_write, + .open = aspeed_mbox_open, + .release = aspeed_mbox_release, + .poll = aspeed_mbox_poll, + .unlocked_ioctl = aspeed_mbox_ioctl, +}; + +static irqreturn_t aspeed_mbox_irq(int irq, void *arg) +{ + struct aspeed_mbox *mbox = arg; + const struct aspeed_mbox_model *model = mbox->model; + + if (!(aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV)) + return IRQ_NONE; + + /* + * Leave the status bit set so that we know the data is for us, + * clear it once it has been read. + */ + + /* Mask it off, we'll clear it when we the data gets read */ + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_MASK, model->bcr); + + wake_up(&mbox->queue); + return IRQ_HANDLED; +} + +static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, + struct platform_device *pdev) +{ + const struct aspeed_mbox_model *model = mbox->model; + struct device *dev = &pdev->dev; + int i, rc, irq; + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) + return -ENODEV; + + rc = devm_request_irq(dev, irq, aspeed_mbox_irq, + IRQF_SHARED, DEVICE_NAME, mbox); + if (rc < 0) { + dev_err(dev, "Unable to request IRQ %d\n", irq); + return rc; + } + + /* + * Disable all register based interrupts. + */ + for (i = 0; i < model->dr_num / 8; ++i) + aspeed_mbox_outb(mbox, 0x00, ASPEED_MBOX_BIE(model->bie, i)); + + /* These registers are write one to clear. Clear them. */ + for (i = 0; i < model->dr_num / 8; ++i) + aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STR(model->str, i)); + + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); + return 0; +} + +static int aspeed_mbox_probe(struct platform_device *pdev) +{ + struct aspeed_mbox *mbox; + struct device *dev; + int rc; + + dev = &pdev->dev; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, mbox); + + rc = of_property_read_u32(dev->of_node, "reg", &mbox->base); + if (rc) { + dev_err(dev, "Couldn't read reg device tree property\n"); + return rc; + } + + mbox->model = of_device_get_match_data(dev); + if (IS_ERR(mbox->model)) { + dev_err(dev, "Couldn't get model data\n"); + return -ENODEV; + } + + mbox->map = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(mbox->map)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + mutex_init(&mbox->mutex); + init_waitqueue_head(&mbox->queue); + + mbox->miscdev.minor = MISC_DYNAMIC_MINOR; + mbox->miscdev.name = DEVICE_NAME; + mbox->miscdev.fops = &aspeed_mbox_fops; + mbox->miscdev.parent = dev; + rc = misc_register(&mbox->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + rc = aspeed_mbox_config_irq(mbox, pdev); + if (rc) { + dev_err(dev, "Failed to configure IRQ\n"); + misc_deregister(&mbox->miscdev); + return rc; + } + + return 0; +} + +static int aspeed_mbox_remove(struct platform_device *pdev) +{ + struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev); + + misc_deregister(&mbox->miscdev); + + return 0; +} + +static const struct aspeed_mbox_model ast2400_model = { + .dr_num = 16, + .dr = 0x0, + .str = 0x40, + .bcr = 0x48, + .hcr = 0x4c, + .bie = 0x50, + .hie = 0x58, +}; + +static const struct aspeed_mbox_model ast2500_model = { + .dr_num = 16, + .dr = 0x0, + .str = 0x40, + .bcr = 0x48, + .hcr = 0x4c, + .bie = 0x50, + .hie = 0x58, +}; + +static const struct aspeed_mbox_model ast2600_model = { + .dr_num = 32, + .dr = 0x0, + .str = 0x80, + .bcr = 0x90, + .hcr = 0x94, + .bie = 0xa0, + .hie = 0xb0, +}; + +static const struct of_device_id aspeed_mbox_match[] = { + { .compatible = "aspeed,ast2400-mbox", + .data = &ast2400_model }, + { .compatible = "aspeed,ast2500-mbox", + .data = &ast2500_model }, + { .compatible = "aspeed,ast2600-mbox", + .data = &ast2600_model }, + { }, +}; + +static struct platform_driver aspeed_mbox_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_mbox_match, + }, + .probe = aspeed_mbox_probe, + .remove = aspeed_mbox_remove, +}; + +module_platform_driver(aspeed_mbox_driver); +MODULE_DEVICE_TABLE(of, aspeed_mbox_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cyril Bur "); +MODULE_AUTHOR("Chia-Wei Wang References: <20210813054758.13189-1-chiawei_wang@aspeedtech.com> Message-ID: <20210813054758.13189-3-chiawei_wang@aspeedtech.com> Add mailbox to the device tree for Aspeed AST24xx/AST25xx/AST26xx SoCs. Signed-off-by: Chia-Wei Wang --- arch/arm/boot/dts/aspeed-g4.dtsi | 7 +++++++ arch/arm/boot/dts/aspeed-g5.dtsi | 8 +++++++- arch/arm/boot/dts/aspeed-g6.dtsi | 7 +++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index c5aeb3cf3a09..6298d69df415 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -383,6 +383,13 @@ interrupts = <8>; status = "disabled"; }; + + mbox: mbox at 200 { + compatible = "aspeed,ast2500-mbox"; + reg = <0x200 0x30>; + interrupts = <46>; + status = "disabled"; + }; }; uart2: serial at 1e78d000 { diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index 329eaeef66fb..ab9453d7803c 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -497,13 +497,19 @@ reg = <0xa0 0x24 0xc8 0x8>; }; - ibt: ibt at 140 { compatible = "aspeed,ast2500-ibt-bmc"; reg = <0x140 0x18>; interrupts = <8>; status = "disabled"; }; + + mbox: mbox at 200 { + compatible = "aspeed,ast2500-mbox"; + reg = <0x200 0x30>; + interrupts = <46>; + status = "disabled"; + }; }; uart2: serial at 1e78d000 { diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index f96607b7b4e2..09b286f2ece2 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -529,6 +529,13 @@ interrupts = ; status = "disabled"; }; + + mbox: mbox at 200 { + compatible = "aspeed,ast2600-mbox"; + reg = <0x200 0xc0>; + interrupts = ; + status = "disabled"; + }; }; sdc: sdc at 1e740000 { -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 13 16:19:00 2021 From: rentao.bupt at gmail.com (rentao.bupt at gmail.com) Date: Thu, 12 Aug 2021 23:19:00 -0700 Subject: [PATCH] ARM: dts: aspeed: cloudripper: Add comments for "mdio1" Message-ID: <20210813061900.24539-1-rentao.bupt@gmail.com> From: Tao Ren Add some comments to explain the purpose of "mdio1" controller: it's connected to the MDC/MDIO interface of the on-board management switch. Signed-off-by: Tao Ren --- arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts index 01ec3ce0a29d..9c6271a17ae8 100644 --- a/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts @@ -96,6 +96,11 @@ status = "okay"; }; +/* + * "mdio1" is connected to the MDC/MDIO interface of the on-board + * management switch (whose ports are connected to BMC, Host and front + * panel ethernet port). + */ &mdio1 { status = "okay"; }; -- 2.17.1 From rentao.bupt at gmail.com Fri Aug 13 16:24:35 2021 From: rentao.bupt at gmail.com (Tao Ren) Date: Thu, 12 Aug 2021 23:24:35 -0700 Subject: [PATCH] ARM: dts: aspeed: minipack: Update flash partition table In-Reply-To: <20210720002704.7390-1-rentao.bupt@gmail.com> References: <20210720002704.7390-1-rentao.bupt@gmail.com> Message-ID: <20210813062435.GA24497@taoren-ubuntu-R90MNF91> Hi Joel, Looks like the patch is not included in "dt-for-v5.15". Any comments? Or should I send v2 if the email was not delivered? Cheers, Tao On Mon, Jul 19, 2021 at 05:27:04PM -0700, rentao.bupt at gmail.com wrote: > From: Tao Ren > > Update firmware flash "data0" partition size from 4MB to 8MB for larger > persistent storage on minipack BMC. > > Signed-off-by: Tao Ren > --- > arch/arm/boot/dts/aspeed-bmc-facebook-minipack.dts | 8 ++++---- > 1 file changed, 4 insertions(+), 4 deletions(-) > > diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-minipack.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-minipack.dts > index 9eb23e874f19..230d16cd9967 100644 > --- a/arch/arm/boot/dts/aspeed-bmc-facebook-minipack.dts > +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-minipack.dts > @@ -265,19 +265,19 @@ > }; > > /* > - * FIT image: 59.5 MB. > + * FIT image: 55.5 MB. > */ > fit at 80000 { > - reg = <0x80000 0x3b80000>; > + reg = <0x80000 0x3780000>; > label = "fit"; > }; > > /* > - * "data0" partition (4MB) is reserved for persistent > + * "data0" partition (8MB) is reserved for persistent > * data store. > */ > data0 at 3800000 { > - reg = <0x3c00000 0x400000>; > + reg = <0x3800000 0x800000>; > label = "data0"; > }; > > -- > 2.17.1 > From p.zabel at pengutronix.de Fri Aug 13 20:02:15 2021 From: p.zabel at pengutronix.de (Philipp Zabel) Date: Fri, 13 Aug 2021 12:02:15 +0200 Subject: [v11 2/2] pwm: Add Aspeed ast2600 PWM support In-Reply-To: <20210812040942.5365-3-billy_tsai@aspeedtech.com> References: <20210812040942.5365-1-billy_tsai@aspeedtech.com> <20210812040942.5365-3-billy_tsai@aspeedtech.com> Message-ID: <5da35c2030e57621fa56795cd193e727f53205fe.camel@pengutronix.de> Hi Billy, On Thu, 2021-08-12 at 12:09 +0800, Billy Tsai wrote: > This patch add the support of PWM controller which can be found at aspeed > ast2600 soc. The pwm supoorts up to 16 channels and it's part function > of multi-function device "pwm-tach controller". > > Signed-off-by: Billy Tsai > --- [...] > diff --git a/drivers/pwm/pwm-aspeed-ast2600.c b/drivers/pwm/pwm-aspeed-ast2600.c > new file mode 100644 > index 000000000000..f89ce1d4cd67 > --- /dev/null > +++ b/drivers/pwm/pwm-aspeed-ast2600.c > @@ -0,0 +1,327 @@ [...] > +static int aspeed_pwm_probe(struct platform_device *pdev) > +{ [...] > + priv->clk = devm_clk_get(&parent_dev->dev, 0); > + if (IS_ERR(priv->clk)) > + return dev_err_probe(dev, PTR_ERR(priv->clk), > + "Couldn't get clock\n"); > + > + ret = clk_prepare_enable(priv->clk); > + if (ret) > + return dev_err_probe(dev, ret, "Couldn't enable clock\n"); > + > + priv->reset = devm_reset_control_get_shared(&parent_dev->dev, NULL); > + if (IS_ERR(priv->reset)) { > + ret = dev_err_probe(dev, PTR_ERR(priv->reset), > + "Get reset failed\n"); > + goto err_disable_clk; > + } I suggest to request the reset control before enabling the clock. That way you can simplify the error path and avoid enabling the clock in case of reset_control_get failure. regards Philipp From andrew at lunn.ch Sat Aug 14 00:45:58 2021 From: andrew at lunn.ch (Andrew Lunn) Date: Fri, 13 Aug 2021 16:45:58 +0200 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: <20210813034016.GA21895@taoren-ubuntu-R90MNF91> References: <20210805222818.8391-1-rentao.bupt@gmail.com> <20210805222818.8391-5-rentao.bupt@gmail.com> <20210813034016.GA21895@taoren-ubuntu-R90MNF91> Message-ID: On Thu, Aug 12, 2021 at 08:40:17PM -0700, Tao Ren wrote: > On Fri, Aug 13, 2021 at 01:29:17AM +0000, Joel Stanley wrote: > > On Thu, 5 Aug 2021 at 22:28, wrote: > > > +&mdio1 { > > > + status = "okay"; > > > > You're enabling this but it looks like it's unused? > > Thanks Joel for the careful review. The MDIO controller is not paired > with BMC MAC; instead, it's connected to the MDC/MDIO interface of the > on-board switch (whose ports are connected to BMC, Host and front panel > management port). What switch is it? Is there a DSA driver for it? drivers/net/dsa/* Ideally you want Linux to be controlling the switch, in the standard linux way. Andrew From rentao.bupt at gmail.com Sat Aug 14 15:22:29 2021 From: rentao.bupt at gmail.com (Tao Ren) Date: Fri, 13 Aug 2021 22:22:29 -0700 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: References: <20210805222818.8391-1-rentao.bupt@gmail.com> <20210805222818.8391-5-rentao.bupt@gmail.com> <20210813034016.GA21895@taoren-ubuntu-R90MNF91> Message-ID: <20210814052228.GA1298@taoren-ubuntu-R90MNF91> Hi Andrew, On Fri, Aug 13, 2021 at 04:45:58PM +0200, Andrew Lunn wrote: > On Thu, Aug 12, 2021 at 08:40:17PM -0700, Tao Ren wrote: > > On Fri, Aug 13, 2021 at 01:29:17AM +0000, Joel Stanley wrote: > > > On Thu, 5 Aug 2021 at 22:28, wrote: > > > > +&mdio1 { > > > > + status = "okay"; > > > > > > You're enabling this but it looks like it's unused? > > > > Thanks Joel for the careful review. The MDIO controller is not paired > > with BMC MAC; instead, it's connected to the MDC/MDIO interface of the > > on-board switch (whose ports are connected to BMC, Host and front panel > > management port). > > What switch is it? Is there a DSA driver for it? drivers/net/dsa/* > Ideally you want Linux to be controlling the switch, in the standard > linux way. > > Andrew Thanks for jumping in. We are using BCM5389 and the MDIO bus is used to access BCM5389 MDC/MDIO interface in Pseudo-PHY mode. I didn't know drivers/net/dsa, but let me check out the drivers and see if it works in the Cloudripper environment. Thanks, Tao From andrew at lunn.ch Sun Aug 15 01:26:55 2021 From: andrew at lunn.ch (Andrew Lunn) Date: Sat, 14 Aug 2021 17:26:55 +0200 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: <20210814052228.GA1298@taoren-ubuntu-R90MNF91> References: <20210805222818.8391-1-rentao.bupt@gmail.com> <20210805222818.8391-5-rentao.bupt@gmail.com> <20210813034016.GA21895@taoren-ubuntu-R90MNF91> <20210814052228.GA1298@taoren-ubuntu-R90MNF91> Message-ID: > Thanks for jumping in. We are using BCM5389 and the MDIO bus is used to > access BCM5389 MDC/MDIO interface in Pseudo-PHY mode. > > I didn't know drivers/net/dsa, but let me check out the drivers and see > if it works in the Cloudripper environment. The b53 driver knows about this switch, so please make use of that driver. See Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml for how you describe it in DT. Andrew From joel at jms.id.au Mon Aug 16 12:11:40 2021 From: joel at jms.id.au (Joel Stanley) Date: Mon, 16 Aug 2021 02:11:40 +0000 Subject: [PATCH] ARM: dts: aspeed: minipack: Update flash partition table In-Reply-To: <20210813062435.GA24497@taoren-ubuntu-R90MNF91> References: <20210720002704.7390-1-rentao.bupt@gmail.com> <20210813062435.GA24497@taoren-ubuntu-R90MNF91> Message-ID: On Fri, 13 Aug 2021 at 06:24, Tao Ren wrote: > > Hi Joel, > > Looks like the patch is not included in "dt-for-v5.15". Any comments? Or > should I send v2 if the email was not delivered? I had missed it. It's now applied for 5.15. Cheers, Joel From billy_tsai at aspeedtech.com Mon Aug 16 20:48:33 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:33 +0800 Subject: [v3 02/15] iio: adc: aspeed: completes the bitfield declare. In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-3-billy_tsai@aspeedtech.com> This patch completes the declare of adc register bitfields and uses the same prefix ASPEED_ADC_* for these bitfields. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 56 +++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 19efaa41bc34..7010d56ac3b9 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -3,6 +3,7 @@ * Aspeed AST2400/2500 ADC * * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2021 Aspeed Technology Inc. */ #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,15 +30,31 @@ #define ASPEED_REG_INTERRUPT_CONTROL 0x04 #define ASPEED_REG_VGA_DETECT_CONTROL 0x08 #define ASPEED_REG_CLOCK_CONTROL 0x0C -#define ASPEED_REG_MAX 0xC0 - -#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) -#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) -#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) - -#define ASPEED_ENGINE_ENABLE BIT(0) - -#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 +#define ASPEED_REG_MAX 0xCC + +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 +#define ASPEED_ADC_OP_MODE_STANDBY 1 +#define ASPEED_ADC_OP_MODE_NORMAL 7 +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_ADC_CH7_MODE BIT(12) +#define ASPEED_ADC_CH7_NORMAL 0 +#define ASPEED_ADC_CH7_BAT 1 +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 @@ -226,7 +244,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (model_data->wait_init_sequence) { /* Enable engine in normal mode. */ - writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); /* Wait for initial sequence complete. */ @@ -245,10 +265,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) goto clk_enable_error; - adc_engine_control_reg_val = GENMASK(31, 16) | - ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + adc_engine_control_reg_val = + ASPEED_ADC_CTRL_CHANNEL | + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); + data->base + ASPEED_REG_ENGINE_CONTROL); model_data = of_device_get_match_data(&pdev->dev); indio_dev->name = model_data->model_name; @@ -264,8 +286,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) return 0; iio_register_error: - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: @@ -283,8 +305,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) struct aspeed_adc_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:31 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:31 +0800 Subject: [v3 00/15] Add support for ast2600 ADC Message-ID: <20210816104846.13155-1-billy_tsai@aspeedtech.com> This patch serials make aspeed_adc.c can support ast2600 and backward compatible. Change since v2: dt-bindings: - Create a new dt-bindings for ast2600 adc aspeed_adc.c: - Splits the patch for more details - Remove version enum and use the flags in model data to distinguish hardware feature - Support trimming data get and set. - Use devm_add_action_or_reset to simplify probe error handling. Changes since v1: dt-bindings: - Fix the aspeed,adc.yaml check error. - Add battery-sensing property. aspeed_adc.c: - Change the init flow: Clock and reference voltage setting should be completed before adc engine enable. - Change the default sampling rate to meet most user case. - Add patch #8 to suppoert battery sensing mode. Billy Tsai (15): dt-bindings: iio: adc: Add ast2600-adc bindings iio: adc: aspeed: completes the bitfield declare. iio: adc: aspeed: set driver data when adc probe. iio: adc: aspeed: Keep model data to driver data. iio: adc: aspeed: Refactory model data structure iio: adc: aspeed: Add vref config function iio: adc: aspeed: Set num_channels with model data iio: adc: aspeed: Use model_data to set clk scaler. iio: adc: aspeed: Use devm_add_action_or_reset. iio: adc: aspeed: Support ast2600 adc. iio: adc: aspeed: Fix the calculate error of clock. iio: adc: aspeed: Add func to set sampling rate. iio: adc: aspeed: Add compensation phase. iio: adc: aspeed: Support battery sensing. iio: adc: aspeed: Get and set trimming data. .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 98 +++ drivers/iio/adc/aspeed_adc.c | 562 +++++++++++++++--- 2 files changed, 570 insertions(+), 90 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:34 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:34 +0800 Subject: [v3 03/15] iio: adc: aspeed: set driver data when adc probe. In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-4-billy_tsai@aspeedtech.com> Fix the issue when adc remove will get the null driver data. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 7010d56ac3b9..20462cf659e4 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -201,6 +201,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:32 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:32 +0800 Subject: [v3 01/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-2-billy_tsai@aspeedtech.com> Add device tree bindings document for the aspeed ast2600 adc device driver. Signed-off-by: Billy Tsai --- .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml new file mode 100644 index 000000000000..28bff89e0d75 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml @@ -0,0 +1,98 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC that forms part of an ASPEED server management processor. + +maintainers: + - Billy Tsai + +description: | + ? 10-bits resolution for 16 voltage channels. + ? The device split into two individual engine and each contains 8 voltage + channels. + ? Channel scanning can be non-continuous. + ? Programmable ADC clock frequency. + ? Programmable upper and lower threshold for each channels. + ? Interrupt when larger or less than threshold for each channels. + ? Support hysteresis for each channels. + ? Built-in a compensating method. + ? Built-in a register to trim internal reference voltage. + ? Internal or External reference voltage. + ? Support 2 Internal reference voltage 1.2v or 2.5v. + ? Integrate dividing circuit for battery sensing. + +properties: + compatible: + enum: + - aspeed,ast2600-adc0 + - aspeed,ast2600-adc1 + + reg: + maxItems: 1 + + clocks: + description: + Input clock used to derive the sample clock. Expected to be the + SoC's APB clock. + + resets: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + vref-supply: + description: + The external regulator supply ADC reference voltage. + + aspeed,int_vref_mv: + enum: + - 1200 + - 2500 + description: + ADC internal reference voltage in millivolts. + + aspeed,battery-sensing: + type: boolean + description: + Inform the driver that last channel will be used to sensor battery. + + aspeed,trim-data-valid: + type: boolean + description: | + The ADC reference voltage can be calibrated to obtain the trimming + data which will be stored in otp. This property informs the driver that + the data store in the otp is valid. + +required: + - compatible + - reg + - clocks + - resets + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include + adc0: adc at 1e6e9000 { + compatible = "aspeed,ast2600-adc0"; + reg = <0x1e6e9000 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int_vref_mv = <2500>; + }; + adc1: adc at 1e6e9100 { + compatible = "aspeed,ast2600-adc1"; + reg = <0x1e6e9100 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int_vref_mv = <2500>; + }; +... -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:38 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:38 +0800 Subject: [v3 07/15] iio: adc: aspeed: Set num_channels with model data In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-8-billy_tsai@aspeedtech.com> Use the model_data member num_channels to set the num_channels of iio device. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index f260e40ab9b2..2d6215a91f99 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -291,7 +291,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); + indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); if (ret) -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:35 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:35 +0800 Subject: [v3 04/15] iio: adc: aspeed: Keep model data to driver data. In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-5-billy_tsai@aspeedtech.com> Keep the model data pointer to driver data for reducing the usage of of_device_get_match_data(). Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 20462cf659e4..d85aa31ee3b1 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -69,6 +69,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; + const struct aspeed_adc_model_data *model_data; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -110,8 +111,6 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -119,7 +118,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = model_data->vref_voltage; + *val = data->model_data->vref_voltage; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -138,13 +137,11 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < model_data->min_sampling_rate || - val > model_data->max_sampling_rate) + if (val < data->model_data->min_sampling_rate || + val > data->model_data->max_sampling_rate) return -EINVAL; clk_set_rate(data->clk_scaler->clk, @@ -190,7 +187,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const struct aspeed_adc_model_data *model_data; const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; @@ -201,6 +197,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + data->model_data = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); @@ -241,9 +238,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); - model_data = of_device_get_match_data(&pdev->dev); - - if (model_data->wait_init_sequence) { + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | @@ -273,8 +268,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); - model_data = of_device_get_match_data(&pdev->dev); - indio_dev->name = model_data->model_name; + indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:36 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:36 +0800 Subject: [v3 05/15] iio: adc: aspeed: Refactory model data structure In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-6-billy_tsai@aspeedtech.com> This patch refactory the model data structure to distinguish the function form differnet version of aspeed adc. - Rename the vref_voltag to vref_fixed and add vref driver data When driver probe will check vref_fixed value and store it to vref which isn't const value. - Add num_channels Make num_channles of iio device can be changed by differnet model_data - Add need_prescaler flag and scaler_bit_width The need_prescaler flag used to tell the driver the clock divider needs another prescaler and the scaler_bit_width to set the clock divider bitfiled width. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index d85aa31ee3b1..f03c7921d534 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -63,8 +63,11 @@ struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz unsigned int max_sampling_rate; // Hz - unsigned int vref_voltage; // mV + unsigned int vref_fixed; // mV bool wait_init_sequence; + bool need_prescaler; + u8 scaler_bit_width; + unsigned int num_channels; }; struct aspeed_adc_data { @@ -75,6 +78,7 @@ struct aspeed_adc_data { struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; + int vref; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -118,7 +122,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_voltage; + *val = data->model_data->vref_fixed; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -312,17 +316,23 @@ static int aspeed_adc_remove(struct platform_device *pdev) static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", - .vref_voltage = 2500, // mV + .vref_fixed = 2500, // mV .min_sampling_rate = 10000, .max_sampling_rate = 500000, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct aspeed_adc_model_data ast2500_model_data = { .model_name = "ast2500-adc", - .vref_voltage = 1800, // mV + .vref_fixed = 1800, // mV .min_sampling_rate = 1, .max_sampling_rate = 1000000, .wait_init_sequence = true, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct of_device_id aspeed_adc_matches[] = { -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:37 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:37 +0800 Subject: [v3 06/15] iio: adc: aspeed: Add vref config function In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-7-billy_tsai@aspeedtech.com> Add the function to check the vref_fixed and set the value to driver data. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index f03c7921d534..f260e40ab9b2 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -122,7 +122,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_fixed; + *val = data->vref; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -187,6 +187,17 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static int aspeed_adc_vref_config(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (data->model_data->vref_fixed) { + data->vref = data->model_data->vref_fixed; + return 0; + } + return 0; +} + static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -242,6 +253,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); + ret = aspeed_adc_vref_config(indio_dev); + if (ret) + goto vref_config_error; + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, @@ -290,6 +305,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: +vref_config_error: reset_control_assert(data->rst); reset_error: clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:40 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:40 +0800 Subject: [v3 09/15] iio: adc: aspeed: Use devm_add_action_or_reset. In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-10-billy_tsai@aspeedtech.com> This patch use devm_add_action_or_reset to handle the error in probe phase. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 92 +++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 52db38be9699..1c87e12a0cab 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -187,6 +187,27 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_unregister_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_divider(clk); +} + +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -232,6 +253,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) &data->clk_lock); if (IS_ERR(data->clk_prescaler)) return PTR_ERR(data->clk_prescaler); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_divider, + data->clk_prescaler); + if (ret) + return ret; snprintf(clk_parent_name, 32, clk_name); scaler_flags = CLK_SET_RATE_PARENT; } @@ -244,23 +271,30 @@ static int aspeed_adc_probe(struct platform_device *pdev) &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, data->model_data->scaler_bit_width, 0, &data->clk_lock); - if (IS_ERR(data->clk_scaler)) { - ret = PTR_ERR(data->clk_scaler); - goto scaler_error; - } + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); + + ret = devm_add_action_or_reset(data->dev, aspeed_adc_unregister_divider, + data->clk_scaler); + if (ret) + return ret; data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); - ret = PTR_ERR(data->rst); - goto reset_error; + return PTR_ERR(data->rst); } reset_control_deassert(data->rst); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, + data->rst); + if (ret) + return ret; + ret = aspeed_adc_vref_config(indio_dev); if (ret) - goto vref_config_error; + return ret; if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ @@ -277,13 +311,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_INIT_POLLING_TIME, ASPEED_ADC_INIT_TIMEOUT); if (ret) - goto poll_timeout_error; + return ret; } /* Start all channels in normal mode. */ ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) - goto clk_enable_error; + return ret; + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; adc_engine_control_reg_val = ASPEED_ADC_CTRL_CHANNEL | @@ -299,41 +339,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); - if (ret) - goto iio_register_error; - + if (ret) { + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); + return ret; + } return 0; - -iio_register_error: - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); -clk_enable_error: -poll_timeout_error: -vref_config_error: - reset_control_assert(data->rst); -reset_error: - clk_hw_unregister_divider(data->clk_scaler); -scaler_error: - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return ret; } static int aspeed_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct aspeed_adc_data *data = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); - reset_control_assert(data->rst); - clk_hw_unregister_divider(data->clk_scaler); - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return 0; } -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 16 20:48:42 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 16 Aug 2021 18:48:42 +0800 Subject: [v3 11/15] iio: adc: aspeed: Fix the calculate error of clock. In-Reply-To: <20210816104846.13155-1-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> Message-ID: <20210816104846.13155-12-billy_tsai@aspeedtech.com> The adc clcok formula is ast2400/2500: ADC clock period = PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) ast2600: ADC clock period = PCLK * 2 * (ADC0C[15:0] + 1) They all have one fixed divided 2 and the legacy driver didn't handle it. This patch register the fixed factory clock device as the parent of adc clock scaler to fix this issue. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index ea3e9a52fcc9..8fe7da1a651f 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -4,6 +4,12 @@ * * Copyright (C) 2017 Google, Inc. * Copyright (C) 2021 Aspeed Technology Inc. + * + * ADC clock formula: + * Ast2400/Ast2500: + * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) + * Ast2600: + * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1) */ #include @@ -77,6 +83,7 @@ struct aspeed_adc_data { struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; + struct clk_hw *fixed_div_clk; struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; @@ -196,6 +203,13 @@ static void aspeed_adc_unregister_divider(void *data) clk_hw_unregister_divider(clk); } +static void aspeed_adc_unregister_fixed_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_fixed_factor(clk); +} + static void aspeed_adc_reset_assert(void *data) { struct reset_control *rst = data; @@ -312,6 +326,18 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Register ADC clock prescaler with source specified by device tree. */ spin_lock_init(&data->clk_lock); snprintf(clk_parent_name, 32, of_clk_get_parent_name(pdev->dev.of_node, 0)); + snprintf(clk_name, 32, "%s-fixed-div", data->model_data->model_name); + data->fixed_div_clk = clk_hw_register_fixed_factor( + &pdev->dev, clk_name, clk_parent_name, 0, 1, 2); + if (IS_ERR(data->fixed_div_clk)) + return PTR_ERR(data->fixed_div_clk); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_fixed_divider, + data->clk_prescaler); + if (ret) + return ret; + snprintf(clk_parent_name, 32, clk_name); if (data->model_data->need_prescaler) { snprintf(clk_name, 32, "%s-prescaler", data->model_data->model_name); -- 2.25.1 From dan.carpenter at oracle.com Mon Aug 16 19:40:55 2021 From: dan.carpenter at oracle.com (Dan Carpenter) Date: Mon, 16 Aug 2021 12:40:55 +0300 Subject: [PATCH 1/2] soc: aspeed: Add LPC mailbox support In-Reply-To: <20210813054758.13189-2-chiawei_wang@aspeedtech.com> Message-ID: <202108160813.GOZ1P4Y8-lkp@intel.com> Hi Chia-Wei, url: https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/aspeed-Add-LPC-mailbox-support/20210813-134908 base: https://git.kernel.org/pub/scm/linux/kernel/git/joel/aspeed.git for-next config: openrisc-randconfig-m031-20210816 (attached as .config) compiler: or1k-linux-gcc (GCC) 11.2.0 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot Reported-by: Dan Carpenter smatch warnings: drivers/soc/aspeed/aspeed-lpc-mbox.c:230 aspeed_mbox_ioctl() warn: maybe return -EFAULT instead of the bytes remaining? vim +230 drivers/soc/aspeed/aspeed-lpc-mbox.c 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 214 static long aspeed_mbox_ioctl(struct file *file, unsigned int cmd, 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 215 unsigned long param) 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 216 { 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 217 struct aspeed_mbox *mbox = file_mbox(file); 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 218 const struct aspeed_mbox_model *model = mbox->model; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 219 struct aspeed_mbox_ioctl_data data; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 220 long ret; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 221 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 222 switch (cmd) { 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 223 case ASPEED_MBOX_IOCTL_GET_SIZE: 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 224 data.data = model->dr_num; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 225 ret = copy_to_user((void __user *)param, &data, sizeof(data)); This should be: if (copy_to_user((void __user *)param, &data, sizeof(data))) return -EFAULT; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 226 break; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 227 default: 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 228 ret = -ENOTTY; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 229 } 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 @230 return ret; 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 231 } --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org From robh at kernel.org Tue Aug 17 06:07:03 2021 From: robh at kernel.org (Rob Herring) Date: Mon, 16 Aug 2021 15:07:03 -0500 Subject: [v3 01/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: <20210816104846.13155-2-billy_tsai@aspeedtech.com> References: <20210816104846.13155-1-billy_tsai@aspeedtech.com> <20210816104846.13155-2-billy_tsai@aspeedtech.com> Message-ID: <1629144423.392245.2563334.nullmailer@robh.at.kernel.org> On Mon, 16 Aug 2021 18:48:32 +0800, Billy Tsai wrote: > Add device tree bindings document for the aspeed ast2600 adc device > driver. > > Signed-off-by: Billy Tsai > --- > .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 98 +++++++++++++++++++ > 1 file changed, 98 insertions(+) > create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml > My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13): yamllint warnings/errors: dtschema/dtc warnings/errors: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml: properties:aspeed,int_vref_mv: 'oneOf' conditional failed, one must be fixed: 'type' is a required property hint: A vendor boolean property can use "type: boolean" Additional properties are not allowed ('enum' was unexpected) hint: A vendor boolean property can use "type: boolean" /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml: properties:aspeed,int_vref_mv: 'oneOf' conditional failed, one must be fixed: '$ref' is a required property 'allOf' is a required property hint: A vendor property needs a $ref to types.yaml from schema $id: http://devicetree.org/meta-schemas/vendor-props.yaml# 1200 is not of type 'string' hint: A vendor string property with exact values has an implicit type 2500 is not of type 'string' hint: A vendor string property with exact values has an implicit type hint: Vendor specific properties must have a type and description unless they have a defined, common suffix. from schema $id: http://devicetree.org/meta-schemas/vendor-props.yaml# /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml: ignoring, error in schema: properties: aspeed,int_vref_mv warning: no schema found in file: ./Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.example.dt.yaml:0:0: /example-0/adc at 1e6e9000: failed to match any schema with compatible: ['aspeed,ast2600-adc0'] Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.example.dt.yaml:0:0: /example-0/adc at 1e6e9100: failed to match any schema with compatible: ['aspeed,ast2600-adc1'] doc reference errors (make refcheckdocs): See https://patchwork.ozlabs.org/patch/1517123 This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit. From rentao.bupt at gmail.com Tue Aug 17 10:01:49 2021 From: rentao.bupt at gmail.com (Tao Ren) Date: Mon, 16 Aug 2021 17:01:49 -0700 Subject: [PATCH v2 4/6] ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC In-Reply-To: References: <20210805222818.8391-1-rentao.bupt@gmail.com> <20210805222818.8391-5-rentao.bupt@gmail.com> <20210813034016.GA21895@taoren-ubuntu-R90MNF91> <20210814052228.GA1298@taoren-ubuntu-R90MNF91> Message-ID: <20210817000148.GA7547@taoren-ubuntu-R90MNF91> On Sat, Aug 14, 2021 at 05:26:55PM +0200, Andrew Lunn wrote: > > Thanks for jumping in. We are using BCM5389 and the MDIO bus is used to > > access BCM5389 MDC/MDIO interface in Pseudo-PHY mode. > > > > I didn't know drivers/net/dsa, but let me check out the drivers and see > > if it works in the Cloudripper environment. > > The b53 driver knows about this switch, so please make use of that > driver. See Documentation/devicetree/bindings/net/dsa/brcm,b53.yaml > for how you describe it in DT. > > Andrew Thank you Andrew for looking into it. I will update dts and send out followup patch after validation (perhaps after v5.15 merge window). Thanks, Tao From rentao.bupt at gmail.com Tue Aug 17 10:04:04 2021 From: rentao.bupt at gmail.com (Tao Ren) Date: Mon, 16 Aug 2021 17:04:04 -0700 Subject: [PATCH] ARM: dts: aspeed: minipack: Update flash partition table In-Reply-To: References: <20210720002704.7390-1-rentao.bupt@gmail.com> <20210813062435.GA24497@taoren-ubuntu-R90MNF91> Message-ID: <20210817000403.GB7547@taoren-ubuntu-R90MNF91> On Mon, Aug 16, 2021 at 02:11:40AM +0000, Joel Stanley wrote: > On Fri, 13 Aug 2021 at 06:24, Tao Ren wrote: > > > > Hi Joel, > > > > Looks like the patch is not included in "dt-for-v5.15". Any comments? Or > > should I send v2 if the email was not delivered? > > I had missed it. It's now applied for 5.15. > > Cheers, > > Joel Thank you Joel. Cheers, Tao From Isaac.Kurth at ibm.com Fri Aug 13 06:54:18 2021 From: Isaac.Kurth at ibm.com (Isaac Kurth) Date: Thu, 12 Aug 2021 20:54:18 +0000 Subject: [PATCH linux dev-5.10] ARM: dts: rainier: Add 'factory-reset-toggle' as GPIOF6 In-Reply-To: <8A669E1A-81B3-40E6-BD0C-342D1566EEC8@linux.ibm.com> Message-ID: An HTML attachment was scrubbed... URL: From lkp at intel.com Mon Aug 16 05:05:15 2021 From: lkp at intel.com (kernel test robot) Date: Mon, 16 Aug 2021 03:05:15 +0800 Subject: [PATCH 1/2] soc: aspeed: Add LPC mailbox support In-Reply-To: <20210813054758.13189-2-chiawei_wang@aspeedtech.com> References: <20210813054758.13189-2-chiawei_wang@aspeedtech.com> Message-ID: <202108160221.59KN8jgQ-lkp@intel.com> Hi Chia-Wei, Thank you for the patch! Perhaps something to improve: [auto build test WARNING on joel-aspeed/for-next] [also build test WARNING on v5.14-rc5 next-20210813] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/aspeed-Add-LPC-mailbox-support/20210813-134908 base: https://git.kernel.org/pub/scm/linux/kernel/git/joel/aspeed.git for-next config: h8300-randconfig-s031-20210816 (attached as .config) compiler: h8300-linux-gcc (GCC) 11.2.0 reproduce: wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # apt-get install sparse # sparse version: v0.6.3-348-gf0e6938b-dirty # https://github.com/0day-ci/linux/commit/72c5a69dc779f5b4af59eb6d47d0e1086be814af git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Chia-Wei-Wang/aspeed-Add-LPC-mailbox-support/20210813-134908 git checkout 72c5a69dc779f5b4af59eb6d47d0e1086be814af # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross C=1 CF='-fdiagnostic-prefix -D__CHECK_ENDIAN__' ARCH=h8300 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot sparse warnings: (new ones prefixed by >>) >> drivers/soc/aspeed/aspeed-lpc-mbox.c:240:27: sparse: sparse: incorrect type in initializer (different base types) @@ expected restricted __poll_t ( *poll )( ... ) @@ got unsigned int ( * )( ... ) @@ drivers/soc/aspeed/aspeed-lpc-mbox.c:240:27: sparse: expected restricted __poll_t ( *poll )( ... ) drivers/soc/aspeed/aspeed-lpc-mbox.c:240:27: sparse: got unsigned int ( * )( ... ) vim +240 drivers/soc/aspeed/aspeed-lpc-mbox.c 232 233 static const struct file_operations aspeed_mbox_fops = { 234 .owner = THIS_MODULE, 235 .llseek = no_seek_end_llseek, 236 .read = aspeed_mbox_read, 237 .write = aspeed_mbox_write, 238 .open = aspeed_mbox_open, 239 .release = aspeed_mbox_release, > 240 .poll = aspeed_mbox_poll, 241 .unlocked_ioctl = aspeed_mbox_ioctl, 242 }; 243 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org -------------- next part -------------- A non-text attachment was scrubbed... Name: .config.gz Type: application/gzip Size: 36610 bytes Desc: not available URL: From jlwright at us.ibm.com Tue Aug 17 02:00:02 2021 From: jlwright at us.ibm.com (Jim Wright) Date: Mon, 16 Aug 2021 11:00:02 -0500 Subject: [PATCH linux dev-5.10] ARM: dts: aspeed: p10bmc: Add power control pins Message-ID: <20210816160002.18645-1-jlwright@us.ibm.com> Add to p10bmc systems the GPIO line names used in chassis power on / off control and chassis power good monitoring. Names used are as documented at [1]. [1] https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md Signed-off-by: Jim Wright --- arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts | 2 +- arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts index 01a0b2b03ddd..941efff7c943 100644 --- a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts +++ b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts @@ -253,7 +253,7 @@ &gpio0 { /*O0-O7*/ "","","","","","","","", /*P0-P7*/ "","","","","led-pcieslot-power","","","", /*Q0-Q7*/ "","","","","","","","", - /*R0-R7*/ "","","","","","I2C_FLASH_MICRO_N","","", + /*R0-R7*/ "","power-chassis-control","power-chassis-good","","","I2C_FLASH_MICRO_N","","", /*S0-S7*/ "","","","","","","","", /*T0-T7*/ "","","","","","","","", /*U0-U7*/ "","","","","","","","", diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts index f87bc5dc8aba..605ad80ef967 100644 --- a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts +++ b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts @@ -243,7 +243,7 @@ &gpio0 { /*O0-O7*/ "","","","usb-power","","","","", /*P0-P7*/ "","","","","pcieslot-power","","","", /*Q0-Q7*/ "cfam-reset","","","","","","","", - /*R0-R7*/ "","","","","","","","", + /*R0-R7*/ "","power-chassis-control","power-chassis-good","","","","","", /*S0-S7*/ "presence-ps0","presence-ps1","presence-ps2","presence-ps3", "","","","", /*T0-T7*/ "","","","","","","","", -- 2.32.0 From andrew at aj.id.au Tue Aug 17 11:49:16 2021 From: andrew at aj.id.au (Andrew Jeffery) Date: Tue, 17 Aug 2021 11:19:16 +0930 Subject: =?UTF-8?Q?Re:_[PATCH_linux_dev-5.10]_ARM:_dts:_aspeed:_p10bmc:_Add_power?= =?UTF-8?Q?_control_pins?= In-Reply-To: <20210816160002.18645-1-jlwright@us.ibm.com> References: <20210816160002.18645-1-jlwright@us.ibm.com> Message-ID: On Tue, 17 Aug 2021, at 01:30, Jim Wright wrote: > Add to p10bmc systems the GPIO line names used in chassis power on / off > control and chassis power good monitoring. Names used are as documented at [1]. > > [1] > https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md > > Signed-off-by: Jim Wright Reviewed-by: Andrew Jeffery From chiawei_wang at aspeedtech.com Tue Aug 17 12:37:13 2021 From: chiawei_wang at aspeedtech.com (ChiaWei Wang) Date: Tue, 17 Aug 2021 02:37:13 +0000 Subject: [PATCH 1/2] soc: aspeed: Add LPC mailbox support In-Reply-To: <202108160813.GOZ1P4Y8-lkp@intel.com> References: <20210813054758.13189-2-chiawei_wang@aspeedtech.com> <202108160813.GOZ1P4Y8-lkp@intel.com> Message-ID: Hi Dan, > -----Original Message----- > From: Dan Carpenter > Sent: Monday, August 16, 2021 5:41 PM > > Hi Chia-Wei, > > url: > https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/aspeed-Add-LPC-m > ailbox-support/20210813-134908 > base: https://git.kernel.org/pub/scm/linux/kernel/git/joel/aspeed.git > for-next > config: openrisc-randconfig-m031-20210816 (attached as .config) > compiler: or1k-linux-gcc (GCC) 11.2.0 > > If you fix the issue, kindly add following tag as appropriate > Reported-by: kernel test robot > Reported-by: Dan Carpenter > > smatch warnings: > drivers/soc/aspeed/aspeed-lpc-mbox.c:230 aspeed_mbox_ioctl() warn: maybe > return -EFAULT instead of the bytes remaining? > > vim +230 drivers/soc/aspeed/aspeed-lpc-mbox.c > > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 214 static long > aspeed_mbox_ioctl(struct file *file, unsigned int cmd, > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 215 > unsigned long param) > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 216 { > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 217 struct aspeed_mbox > *mbox = file_mbox(file); > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 218 const struct > aspeed_mbox_model *model = mbox->model; > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 219 struct > aspeed_mbox_ioctl_data data; > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 220 long ret; > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 221 > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 222 switch (cmd) { > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 223 case > ASPEED_MBOX_IOCTL_GET_SIZE: > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 224 data.data = > model->dr_num; > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 225 ret = > copy_to_user((void __user *)param, &data, sizeof(data)); > > This should be: > > if (copy_to_user((void __user *)param, &data, sizeof(data))) > return -EFAULT; Will be fixed in v2 patch. Thanks for the review. Regards, Chiawei > > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 226 break; > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 227 default: > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 228 ret = -ENOTTY; > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 229 } > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 @230 return ret; > 72c5a69dc779f5 Chia-Wei Wang 2021-08-13 231 } > > --- > 0-DAY CI Kernel Test Service, Intel Corporation > https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org From chiawei_wang at aspeedtech.com Tue Aug 17 12:58:48 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Tue, 17 Aug 2021 10:58:48 +0800 Subject: [PATCH v2 2/2] ARM: dts: aspeed: Add mailbox to device tree In-Reply-To: <20210817025848.19914-1-chiawei_wang@aspeedtech.com> References: <20210817025848.19914-1-chiawei_wang@aspeedtech.com> Message-ID: <20210817025848.19914-3-chiawei_wang@aspeedtech.com> Add mailbox to the device tree for Aspeed AST24xx/AST25xx/AST26xx SoCs. Signed-off-by: Chia-Wei Wang --- arch/arm/boot/dts/aspeed-g4.dtsi | 7 +++++++ arch/arm/boot/dts/aspeed-g5.dtsi | 8 +++++++- arch/arm/boot/dts/aspeed-g6.dtsi | 7 +++++++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/arch/arm/boot/dts/aspeed-g4.dtsi b/arch/arm/boot/dts/aspeed-g4.dtsi index c5aeb3cf3a09..6298d69df415 100644 --- a/arch/arm/boot/dts/aspeed-g4.dtsi +++ b/arch/arm/boot/dts/aspeed-g4.dtsi @@ -383,6 +383,13 @@ interrupts = <8>; status = "disabled"; }; + + mbox: mbox at 200 { + compatible = "aspeed,ast2500-mbox"; + reg = <0x200 0x30>; + interrupts = <46>; + status = "disabled"; + }; }; uart2: serial at 1e78d000 { diff --git a/arch/arm/boot/dts/aspeed-g5.dtsi b/arch/arm/boot/dts/aspeed-g5.dtsi index 329eaeef66fb..ab9453d7803c 100644 --- a/arch/arm/boot/dts/aspeed-g5.dtsi +++ b/arch/arm/boot/dts/aspeed-g5.dtsi @@ -497,13 +497,19 @@ reg = <0xa0 0x24 0xc8 0x8>; }; - ibt: ibt at 140 { compatible = "aspeed,ast2500-ibt-bmc"; reg = <0x140 0x18>; interrupts = <8>; status = "disabled"; }; + + mbox: mbox at 200 { + compatible = "aspeed,ast2500-mbox"; + reg = <0x200 0x30>; + interrupts = <46>; + status = "disabled"; + }; }; uart2: serial at 1e78d000 { diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index f96607b7b4e2..09b286f2ece2 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -529,6 +529,13 @@ interrupts = ; status = "disabled"; }; + + mbox: mbox at 200 { + compatible = "aspeed,ast2600-mbox"; + reg = <0x200 0xc0>; + interrupts = ; + status = "disabled"; + }; }; sdc: sdc at 1e740000 { -- 2.17.1 From chiawei_wang at aspeedtech.com Tue Aug 17 12:58:46 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Tue, 17 Aug 2021 10:58:46 +0800 Subject: [PATCH v2 0/2] aspeed: Add LPC mailbox support Message-ID: <20210817025848.19914-1-chiawei_wang@aspeedtech.com> Add driver support for the LPC mailbox controller of ASPEED SoCs. v2: - Fix error handling for copy_to_user - Fix incorrect type in the .poll initializer Chia-Wei Wang (2): soc: aspeed: Add LPC mailbox support ARM: dts: aspeed: Add mailbox to device tree arch/arm/boot/dts/aspeed-g4.dtsi | 7 + arch/arm/boot/dts/aspeed-g5.dtsi | 8 +- arch/arm/boot/dts/aspeed-g6.dtsi | 7 + drivers/soc/aspeed/Kconfig | 10 + drivers/soc/aspeed/Makefile | 9 +- drivers/soc/aspeed/aspeed-lpc-mbox.c | 418 +++++++++++++++++++++++++++ 6 files changed, 454 insertions(+), 5 deletions(-) create mode 100644 drivers/soc/aspeed/aspeed-lpc-mbox.c -- 2.17.1 From chiawei_wang at aspeedtech.com Tue Aug 17 12:58:47 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Tue, 17 Aug 2021 10:58:47 +0800 Subject: [PATCH v2 1/2] soc: aspeed: Add LPC mailbox support In-Reply-To: <20210817025848.19914-1-chiawei_wang@aspeedtech.com> References: <20210817025848.19914-1-chiawei_wang@aspeedtech.com> Message-ID: <20210817025848.19914-2-chiawei_wang@aspeedtech.com> The LPC mailbox controller consists of multiple general registers for the communication between the Host and the BMC. The interrupts for data update signaling are also introduced. Signed-off-by: Chia-Wei Wang Reported-by: kernel test robot Reported-by: Dan Carpenter --- drivers/soc/aspeed/Kconfig | 10 + drivers/soc/aspeed/Makefile | 9 +- drivers/soc/aspeed/aspeed-lpc-mbox.c | 418 +++++++++++++++++++++++++++ 3 files changed, 433 insertions(+), 4 deletions(-) create mode 100644 drivers/soc/aspeed/aspeed-lpc-mbox.c diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index 243ca196e6ad..5b31a6e2620c 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -24,6 +24,16 @@ config ASPEED_LPC_SNOOP allows the BMC to listen on and save the data written by the host to an arbitrary LPC I/O port. +config ASPEED_LPC_MAILBOX + tristate "ASPEED LPC mailbox support" + select REGMAP + select MFD_SYSCON + default ARCH_ASPEED + help + Provides a driver to control the LPC mailbox which possesses + up to 32 data registers for the communication between the Host + and the BMC over LPC. + config ASPEED_P2A_CTRL tristate "ASPEED P2A (VGA MMIO to BMC) bridge control" select REGMAP diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index fcab7192e1a4..cde6b4514c97 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o -obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o -obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o -obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o +obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o +obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o +obj-$(CONFIG_ASPEED_LPC_MAILBOX) += aspeed-lpc-mbox.o +obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o +obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o diff --git a/drivers/soc/aspeed/aspeed-lpc-mbox.c b/drivers/soc/aspeed/aspeed-lpc-mbox.c new file mode 100644 index 000000000000..a09ca6a175f7 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-lpc-mbox.c @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2017 IBM Corporation + * Copyright 2021 Aspeed Technology Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "aspeed-mbox" + +#define ASPEED_MBOX_DR(dr, n) (dr + (n * 4)) +#define ASPEED_MBOX_STR(str, n) (str + (n / 8) * 4) +#define ASPEED_MBOX_BIE(bie, n) (bie + (n / 8) * 4) +#define ASPEED_MBOX_HIE(hie, n) (hie + (n / 8) * 4) + +#define ASPEED_MBOX_BCR_RECV BIT(7) +#define ASPEED_MBOX_BCR_MASK BIT(1) +#define ASPEED_MBOX_BCR_SEND BIT(0) + +/* ioctl code */ +#define ASPEED_MBOX_IOCTL 0xA3 +#define ASPEED_MBOX_IOCTL_GET_SIZE \ + _IOR(ASPEED_MBOX_IOCTL, 0, struct aspeed_mbox_ioctl_data) + +struct aspeed_mbox_ioctl_data { + unsigned int data; +}; + +struct aspeed_mbox_model { + unsigned int dr_num; + + /* offsets to the MBOX registers */ + unsigned int dr; + unsigned int str; + unsigned int bcr; + unsigned int hcr; + unsigned int bie; + unsigned int hie; +}; + +struct aspeed_mbox { + struct miscdevice miscdev; + struct regmap *map; + unsigned int base; + wait_queue_head_t queue; + struct mutex mutex; + const struct aspeed_mbox_model *model; +}; + +static atomic_t aspeed_mbox_open_count = ATOMIC_INIT(0); + +static u8 aspeed_mbox_inb(struct aspeed_mbox *mbox, int reg) +{ + /* + * The mbox registers are actually only one byte but are addressed + * four bytes apart. The other three bytes are marked 'reserved', + * they *should* be zero but lets not rely on it. + * I am going to rely on the fact we can casually read/write to them... + */ + unsigned int val = 0xff; /* If regmap throws an error return 0xff */ + int rc = regmap_read(mbox->map, mbox->base + reg, &val); + + if (rc) + dev_err(mbox->miscdev.parent, "regmap_read() failed with " + "%d (reg: 0x%08x)\n", rc, reg); + + return val & 0xff; +} + +static void aspeed_mbox_outb(struct aspeed_mbox *mbox, u8 data, int reg) +{ + int rc = regmap_write(mbox->map, mbox->base + reg, data); + + if (rc) + dev_err(mbox->miscdev.parent, "regmap_write() failed with " + "%d (data: %u reg: 0x%08x)\n", rc, data, reg); +} + +static struct aspeed_mbox *file_mbox(struct file *file) +{ + return container_of(file->private_data, struct aspeed_mbox, miscdev); +} + +static int aspeed_mbox_open(struct inode *inode, struct file *file) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + + if (atomic_inc_return(&aspeed_mbox_open_count) == 1) { + /* + * Clear the interrupt status bit if it was left on and unmask + * interrupts. + * ASPEED_MBOX_BCR_RECV bit is W1C, this also unmasks in 1 step + */ + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); + return 0; + } + + atomic_dec(&aspeed_mbox_open_count); + return -EBUSY; +} + +static ssize_t aspeed_mbox_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + char __user *p = buf; + ssize_t ret; + int i; + + if (!access_ok(buf, count)) + return -EFAULT; + + if (count + *ppos > model->dr_num) + return -EINVAL; + + if (file->f_flags & O_NONBLOCK) { + if (!(aspeed_mbox_inb(mbox, model->bcr) & + ASPEED_MBOX_BCR_RECV)) + return -EAGAIN; + } else if (wait_event_interruptible(mbox->queue, + aspeed_mbox_inb(mbox, model->bcr) & + ASPEED_MBOX_BCR_RECV)) { + return -ERESTARTSYS; + } + + mutex_lock(&mbox->mutex); + + for (i = *ppos; count > 0 && i < model->dr_num; i++) { + uint8_t reg = aspeed_mbox_inb(mbox, ASPEED_MBOX_DR(model->dr, i)); + + ret = __put_user(reg, p); + if (ret) + goto out_unlock; + + p++; + count--; + } + + /* ASPEED_MBOX_BCR_RECV bit is write to clear, this also unmasks in 1 step */ + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); + ret = p - buf; + +out_unlock: + mutex_unlock(&mbox->mutex); + return ret; +} + +static ssize_t aspeed_mbox_write(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + const char __user *p = buf; + ssize_t ret; + char c; + int i; + + if (!access_ok(buf, count)) + return -EFAULT; + + if (count + *ppos > model->dr_num) + return -EINVAL; + + mutex_lock(&mbox->mutex); + + for (i = *ppos; count > 0 && i < model->dr_num; i++) { + ret = __get_user(c, p); + if (ret) + goto out_unlock; + + aspeed_mbox_outb(mbox, c, ASPEED_MBOX_DR(model->dr, i)); + p++; + count--; + } + + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_SEND, model->bcr); + ret = p - buf; + +out_unlock: + mutex_unlock(&mbox->mutex); + return ret; +} + +static __poll_t aspeed_mbox_poll(struct file *file, poll_table *wait) +{ + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + __poll_t mask = 0; + + poll_wait(file, &mbox->queue, wait); + + if (aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV) + mask |= POLLIN; + + return mask; +} + +static int aspeed_mbox_release(struct inode *inode, struct file *file) +{ + atomic_dec(&aspeed_mbox_open_count); + return 0; +} + +static long aspeed_mbox_ioctl(struct file *file, unsigned int cmd, + unsigned long param) +{ + long ret = 0; + struct aspeed_mbox *mbox = file_mbox(file); + const struct aspeed_mbox_model *model = mbox->model; + struct aspeed_mbox_ioctl_data data; + + switch (cmd) { + case ASPEED_MBOX_IOCTL_GET_SIZE: + data.data = model->dr_num; + if (copy_to_user((void __user *)param, &data, sizeof(data))) + ret = -EFAULT; + break; + default: + ret = -ENOTTY; + break; + } + + return ret; +} + +static const struct file_operations aspeed_mbox_fops = { + .owner = THIS_MODULE, + .llseek = no_seek_end_llseek, + .read = aspeed_mbox_read, + .write = aspeed_mbox_write, + .open = aspeed_mbox_open, + .release = aspeed_mbox_release, + .poll = aspeed_mbox_poll, + .unlocked_ioctl = aspeed_mbox_ioctl, +}; + +static irqreturn_t aspeed_mbox_irq(int irq, void *arg) +{ + struct aspeed_mbox *mbox = arg; + const struct aspeed_mbox_model *model = mbox->model; + + if (!(aspeed_mbox_inb(mbox, model->bcr) & ASPEED_MBOX_BCR_RECV)) + return IRQ_NONE; + + /* + * Leave the status bit set so that we know the data is for us, + * clear it once it has been read. + */ + + /* Mask it off, we'll clear it when we the data gets read */ + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_MASK, model->bcr); + + wake_up(&mbox->queue); + return IRQ_HANDLED; +} + +static int aspeed_mbox_config_irq(struct aspeed_mbox *mbox, + struct platform_device *pdev) +{ + const struct aspeed_mbox_model *model = mbox->model; + struct device *dev = &pdev->dev; + int i, rc, irq; + + irq = irq_of_parse_and_map(dev->of_node, 0); + if (!irq) + return -ENODEV; + + rc = devm_request_irq(dev, irq, aspeed_mbox_irq, + IRQF_SHARED, DEVICE_NAME, mbox); + if (rc < 0) { + dev_err(dev, "Unable to request IRQ %d\n", irq); + return rc; + } + + /* + * Disable all register based interrupts. + */ + for (i = 0; i < model->dr_num / 8; ++i) + aspeed_mbox_outb(mbox, 0x00, ASPEED_MBOX_BIE(model->bie, i)); + + /* These registers are write one to clear. Clear them. */ + for (i = 0; i < model->dr_num / 8; ++i) + aspeed_mbox_outb(mbox, 0xff, ASPEED_MBOX_STR(model->str, i)); + + aspeed_mbox_outb(mbox, ASPEED_MBOX_BCR_RECV, model->bcr); + return 0; +} + +static int aspeed_mbox_probe(struct platform_device *pdev) +{ + struct aspeed_mbox *mbox; + struct device *dev; + int rc; + + dev = &pdev->dev; + + mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); + if (!mbox) + return -ENOMEM; + + dev_set_drvdata(&pdev->dev, mbox); + + rc = of_property_read_u32(dev->of_node, "reg", &mbox->base); + if (rc) { + dev_err(dev, "Couldn't read reg device tree property\n"); + return rc; + } + + mbox->model = of_device_get_match_data(dev); + if (IS_ERR(mbox->model)) { + dev_err(dev, "Couldn't get model data\n"); + return -ENODEV; + } + + mbox->map = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(mbox->map)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + mutex_init(&mbox->mutex); + init_waitqueue_head(&mbox->queue); + + mbox->miscdev.minor = MISC_DYNAMIC_MINOR; + mbox->miscdev.name = DEVICE_NAME; + mbox->miscdev.fops = &aspeed_mbox_fops; + mbox->miscdev.parent = dev; + rc = misc_register(&mbox->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + return rc; + } + + rc = aspeed_mbox_config_irq(mbox, pdev); + if (rc) { + dev_err(dev, "Failed to configure IRQ\n"); + misc_deregister(&mbox->miscdev); + return rc; + } + + return 0; +} + +static int aspeed_mbox_remove(struct platform_device *pdev) +{ + struct aspeed_mbox *mbox = dev_get_drvdata(&pdev->dev); + + misc_deregister(&mbox->miscdev); + + return 0; +} + +static const struct aspeed_mbox_model ast2400_model = { + .dr_num = 16, + .dr = 0x0, + .str = 0x40, + .bcr = 0x48, + .hcr = 0x4c, + .bie = 0x50, + .hie = 0x58, +}; + +static const struct aspeed_mbox_model ast2500_model = { + .dr_num = 16, + .dr = 0x0, + .str = 0x40, + .bcr = 0x48, + .hcr = 0x4c, + .bie = 0x50, + .hie = 0x58, +}; + +static const struct aspeed_mbox_model ast2600_model = { + .dr_num = 32, + .dr = 0x0, + .str = 0x80, + .bcr = 0x90, + .hcr = 0x94, + .bie = 0xa0, + .hie = 0xb0, +}; + +static const struct of_device_id aspeed_mbox_match[] = { + { .compatible = "aspeed,ast2400-mbox", + .data = &ast2400_model }, + { .compatible = "aspeed,ast2500-mbox", + .data = &ast2500_model }, + { .compatible = "aspeed,ast2600-mbox", + .data = &ast2600_model }, + { }, +}; + +static struct platform_driver aspeed_mbox_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_mbox_match, + }, + .probe = aspeed_mbox_probe, + .remove = aspeed_mbox_remove, +}; + +module_platform_driver(aspeed_mbox_driver); +MODULE_DEVICE_TABLE(of, aspeed_mbox_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cyril Bur "); +MODULE_AUTHOR("Chia-Wei Wang Hello Soc maintainers, Here are the aspeed changes for 5.15. The following changes since commit 2734d6c1b1a089fb593ef6a23d4b70903526fe0c: Linux 5.14-rc2 (2021-07-18 14:13:49 -0700) are available in the Git repository at: git://git.kernel.org/pub/scm/linux/kernel/git/joel/bmc.git tags/aspeed-5.15-devicetree for you to fetch changes up to 6b8b31269898b8891f1f3a95578ceeb109393e23: ARM: dts: aspeed: p10bmc: Add power control pins (2021-08-17 13:52:46 +0930) ---------------------------------------------------------------- ASPEED device tree updates for 5.15 - New machines * Facebook's Cloudripper * Facebook's Elbert * Facebook's Fuji All three carry the description of "Facebook's next generation switch platform with an AST2600 BMC integrated for health monitoring purpose." They share a 128 MB SPI NOR flash layout that is also used by some older platforms. * Inspur's NF5280M6, an x86 platform server with an AST2500-based BMC - SGPIO updates including AST2600 support - GPIO descriptions for the IBM AST2600 machines - Pinctrl fix - Updates to Facebook's AST2500 based machines ---------------------------------------------------------------- Andrew Jeffery (1): ARM: dts: everest: Add phase corrections for eMMC Dylan Hung (1): ARM: dts: aspeed-g6: Fix HVI3C function-group in pinctrl dtsi Jim Wright (1): ARM: dts: aspeed: p10bmc: Add power control pins Joel Stanley (3): ARM: dts: rainier, everest: Add TPM reset GPIO ARM: dts: aspeed: tacoma: Add TPM reset GPIO ARM: dts: aspeed: ast2500evb: Enable built in RTC Ping Guo (1): ARM: dts: aspeed: Add Inspur NF5280M6 BMC machine Steven Lee (4): dt-bindings: aspeed-sgpio: Convert txt bindings to yaml. dt-bindings: aspeed-sgpio: Add ast2600 sgpio ARM: dts: aspeed-g6: Add SGPIO node. ARM: dts: aspeed-g5: Remove ngpios from sgpio node. Tao Ren (12): ARM: dts: aspeed: Enable ADC in Facebook AST2400 common dtsi ARM: dts: aspeed: wedge40: Remove redundant ADC device ARM: dts: aspeed: galaxy100: Remove redundant ADC device ARM: dts: aspeed: wedge100: Enable ADC channels ARM: dts: Add Facebook BMC 128MB flash layout ARM: dts: aspeed: wedge400: Use common flash layout ARM: dts: aspeed: Common dtsi for Facebook AST2600 Network BMCs ARM: dts: aspeed: Add Facebook Cloudripper (AST2600) BMC ARM: dts: aspeed: Add Facebook Elbert (AST2600) BMC ARM: dts: aspeed: Add Facebook Fuji (AST2600) BMC ARM: dts: aspeed: minipack: Update flash partition table ARM: dts: aspeed: cloudripper: Add comments for "mdio1" .../devicetree/bindings/gpio/aspeed,sgpio.yaml | 77 ++ .../devicetree/bindings/gpio/sgpio-aspeed.txt | 46 - arch/arm/boot/dts/Makefile | 4 + arch/arm/boot/dts/aspeed-ast2500-evb.dts | 4 + .../boot/dts/aspeed-bmc-facebook-cloudripper.dts | 544 +++++++++ arch/arm/boot/dts/aspeed-bmc-facebook-elbert.dts | 185 +++ arch/arm/boot/dts/aspeed-bmc-facebook-fuji.dts | 1251 ++++++++++++++++++++ .../arm/boot/dts/aspeed-bmc-facebook-galaxy100.dts | 4 - arch/arm/boot/dts/aspeed-bmc-facebook-minipack.dts | 8 +- arch/arm/boot/dts/aspeed-bmc-facebook-wedge100.dts | 5 + arch/arm/boot/dts/aspeed-bmc-facebook-wedge40.dts | 4 - arch/arm/boot/dts/aspeed-bmc-facebook-wedge400.dts | 48 +- arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts | 4 +- arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts | 2 +- arch/arm/boot/dts/aspeed-bmc-inspur-nf5280m6.dts | 691 +++++++++++ arch/arm/boot/dts/aspeed-bmc-opp-tacoma.dts | 2 +- arch/arm/boot/dts/aspeed-g5.dtsi | 1 - arch/arm/boot/dts/aspeed-g6-pinctrl.dtsi | 4 +- arch/arm/boot/dts/aspeed-g6.dtsi | 28 + .../boot/dts/ast2400-facebook-netbmc-common.dtsi | 4 + .../boot/dts/ast2600-facebook-netbmc-common.dtsi | 169 +++ .../boot/dts/facebook-bmc-flash-layout-128.dtsi | 60 + 22 files changed, 3033 insertions(+), 112 deletions(-) create mode 100644 Documentation/devicetree/bindings/gpio/aspeed,sgpio.yaml delete mode 100644 Documentation/devicetree/bindings/gpio/sgpio-aspeed.txt create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-elbert.dts create mode 100644 arch/arm/boot/dts/aspeed-bmc-facebook-fuji.dts create mode 100644 arch/arm/boot/dts/aspeed-bmc-inspur-nf5280m6.dts create mode 100644 arch/arm/boot/dts/ast2600-facebook-netbmc-common.dtsi create mode 100644 arch/arm/boot/dts/facebook-bmc-flash-layout-128.dtsilin From joel at jms.id.au Tue Aug 17 18:21:04 2021 From: joel at jms.id.au (Joel Stanley) Date: Tue, 17 Aug 2021 08:21:04 +0000 Subject: [PATCH] ARM: dts: aspeed: cloudripper: Add comments for "mdio1" In-Reply-To: <20210813061900.24539-1-rentao.bupt@gmail.com> References: <20210813061900.24539-1-rentao.bupt@gmail.com> Message-ID: On Fri, 13 Aug 2021 at 06:19, wrote: > > From: Tao Ren > > Add some comments to explain the purpose of "mdio1" controller: it's > connected to the MDC/MDIO interface of the on-board management switch. > > Signed-off-by: Tao Ren Thanks, applied. > --- > arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts | 5 +++++ > 1 file changed, 5 insertions(+) > > diff --git a/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts b/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts > index 01ec3ce0a29d..9c6271a17ae8 100644 > --- a/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts > +++ b/arch/arm/boot/dts/aspeed-bmc-facebook-cloudripper.dts > @@ -96,6 +96,11 @@ > status = "okay"; > }; > > +/* > + * "mdio1" is connected to the MDC/MDIO interface of the on-board > + * management switch (whose ports are connected to BMC, Host and front > + * panel ethernet port). > + */ > &mdio1 { > status = "okay"; > }; > -- > 2.17.1 > From joel at jms.id.au Tue Aug 17 18:22:31 2021 From: joel at jms.id.au (Joel Stanley) Date: Tue, 17 Aug 2021 08:22:31 +0000 Subject: [PATCH linux dev-5.10] ARM: dts: aspeed: p10bmc: Add power control pins In-Reply-To: <20210816160002.18645-1-jlwright@us.ibm.com> References: <20210816160002.18645-1-jlwright@us.ibm.com> Message-ID: On Mon, 16 Aug 2021 at 16:01, Jim Wright wrote: > > Add to p10bmc systems the GPIO line names used in chassis power on / off > control and chassis power good monitoring. Names used are as documented at [1]. > > [1] https://github.com/openbmc/docs/blob/master/designs/device-tree-gpio-naming.md > > Signed-off-by: Jim Wright Thanks, applied. > --- > arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts | 2 +- > arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts | 2 +- > 2 files changed, 2 insertions(+), 2 deletions(-) > > diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts > index 01a0b2b03ddd..941efff7c943 100644 > --- a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts > +++ b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts > @@ -253,7 +253,7 @@ &gpio0 { > /*O0-O7*/ "","","","","","","","", > /*P0-P7*/ "","","","","led-pcieslot-power","","","", > /*Q0-Q7*/ "","","","","","","","", > - /*R0-R7*/ "","","","","","I2C_FLASH_MICRO_N","","", > + /*R0-R7*/ "","power-chassis-control","power-chassis-good","","","I2C_FLASH_MICRO_N","","", > /*S0-S7*/ "","","","","","","","", > /*T0-T7*/ "","","","","","","","", > /*U0-U7*/ "","","","","","","","", > diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts > index f87bc5dc8aba..605ad80ef967 100644 > --- a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts > +++ b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts > @@ -243,7 +243,7 @@ &gpio0 { > /*O0-O7*/ "","","","usb-power","","","","", > /*P0-P7*/ "","","","","pcieslot-power","","","", > /*Q0-Q7*/ "cfam-reset","","","","","","","", > - /*R0-R7*/ "","","","","","","","", > + /*R0-R7*/ "","power-chassis-control","power-chassis-good","","","","","", > /*S0-S7*/ "presence-ps0","presence-ps1","presence-ps2","presence-ps3", > "","","","", > /*T0-T7*/ "","","","","","","","", > -- > 2.32.0 > From joel at jms.id.au Wed Aug 18 11:05:34 2021 From: joel at jms.id.au (Joel Stanley) Date: Wed, 18 Aug 2021 10:35:34 +0930 Subject: [PATCH] soc: aspeed: socinfo: Add AST2625 variant Message-ID: <20210818010534.2508122-1-joel@jms.id.au> Add AST26XX series AST2625-A3 SOC ID, taken from the vendor u-boot SDK: arch/arm/mach-aspeed/ast2600/scu_info.c + SOC_ID("AST2625-A3", 0x0503040305030403), Signed-off-by: Joel Stanley --- Dylan, can you please review this for me? drivers/soc/aspeed/aspeed-socinfo.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/soc/aspeed/aspeed-socinfo.c b/drivers/soc/aspeed/aspeed-socinfo.c index e3215f826d17..1ca140356a08 100644 --- a/drivers/soc/aspeed/aspeed-socinfo.c +++ b/drivers/soc/aspeed/aspeed-socinfo.c @@ -26,6 +26,7 @@ static struct { { "AST2600", 0x05000303 }, { "AST2620", 0x05010203 }, { "AST2605", 0x05030103 }, + { "AST2625", 0x05030403 }, }; static const char *siliconid_to_name(u32 siliconid) -- 2.32.0 From arnd at kernel.org Wed Aug 18 22:59:25 2021 From: arnd at kernel.org (Arnd Bergmann) Date: Wed, 18 Aug 2021 14:59:25 +0200 Subject: [GIT PULL] ARM: aspeed: devicetree changes for 5.15 In-Reply-To: References: Message-ID: <162929137186.372162.11203697127775599651.b4-ty@arndb.de> From: Arnd Bergmann On Tue, 17 Aug 2021 07:51:54 +0000, Joel Stanley wrote: > Here are the aspeed changes for 5.15. > > The following changes since commit 2734d6c1b1a089fb593ef6a23d4b70903526fe0c: > > Linux 5.14-rc2 (2021-07-18 14:13:49 -0700) > > are available in the Git repository at: > > [...] Merged into arm/dt, thanks! merge commit: c872138c2c7166dc95c778f4decc165bace9cb33 Arnd From dylan_hung at aspeedtech.com Thu Aug 19 13:29:27 2021 From: dylan_hung at aspeedtech.com (Dylan Hung) Date: Thu, 19 Aug 2021 03:29:27 +0000 Subject: [PATCH] soc: aspeed: socinfo: Add AST2625 variant In-Reply-To: <20210818010534.2508122-1-joel@jms.id.au> References: <20210818010534.2508122-1-joel@jms.id.au> Message-ID: > -----Original Message----- > From: joel.stan at gmail.com [mailto:joel.stan at gmail.com] On Behalf Of Joel > Stanley > Sent: 2021?8?18? 9:06 AM > To: Dylan Hung ; Andrew Jeffery > > Cc: linux-arm-kernel at lists.infradead.org; linux-aspeed at lists.ozlabs.org > Subject: [PATCH] soc: aspeed: socinfo: Add AST2625 variant > > Add AST26XX series AST2625-A3 SOC ID, taken from the vendor u-boot SDK: > > arch/arm/mach-aspeed/ast2600/scu_info.c > + SOC_ID("AST2625-A3", 0x0503040305030403), > > Signed-off-by: Joel Stanley > --- > Dylan, can you please review this for me? > > drivers/soc/aspeed/aspeed-socinfo.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/drivers/soc/aspeed/aspeed-socinfo.c > b/drivers/soc/aspeed/aspeed-socinfo.c > index e3215f826d17..1ca140356a08 100644 > --- a/drivers/soc/aspeed/aspeed-socinfo.c > +++ b/drivers/soc/aspeed/aspeed-socinfo.c > @@ -26,6 +26,7 @@ static struct { > { "AST2600", 0x05000303 }, > { "AST2620", 0x05010203 }, > { "AST2605", 0x05030103 }, > + { "AST2625", 0x05030403 }, > }; > > static const char *siliconid_to_name(u32 siliconid) > -- > 2.32.0 Reviewed-by: Dylan Hung From joel at jms.id.au Thu Aug 19 15:31:53 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 19 Aug 2021 05:31:53 +0000 Subject: [GIT PULL] ARM: aspeed: soc changes for 5.15 Message-ID: Hello Soc maintainers, Here are some soc changes for 5.15. The following changes since commit 2734d6c1b1a089fb593ef6a23d4b70903526fe0c: Linux 5.14-rc2 (2021-07-18 14:13:49 -0700) are available in the Git repository at: git://git.kernel.org/pub/scm/linux/kernel/git/joel/bmc.git tags/aspeed-5.15-soc for you to fetch changes up to 2f9b25fa668218f22a85ebe8c55d1d132fc0019d: soc: aspeed: Re-enable FWH2AHB on AST2600 (2021-08-19 13:59:28 +0930) ---------------------------------------------------------------- ASPEED soc updates for 5.15 * Two fixes for drivers that control the mapping of BMC memory over PCIe and LPC * Re-enable FWH2AHB on systems that have it closed off by default * A new id for the AST2625, an AST2600 variant ---------------------------------------------------------------- Iwona Winiarska (2): soc: aspeed: lpc-ctrl: Fix boundary check for mmap soc: aspeed: p2a-ctrl: Fix boundary check for mmap Joel Stanley (2): soc: aspeed: socinfo: Add AST2625 variant soc: aspeed: Re-enable FWH2AHB on AST2600 drivers/soc/aspeed/aspeed-lpc-ctrl.c | 31 ++++++++++++++++++++++++------- drivers/soc/aspeed/aspeed-p2a-ctrl.c | 2 +- drivers/soc/aspeed/aspeed-socinfo.c | 1 + 3 files changed, 26 insertions(+), 8 deletions(-) From liuxiwei1013 at gmail.com Thu Aug 19 15:49:08 2021 From: liuxiwei1013 at gmail.com (George Liu) Date: Thu, 19 Aug 2021 13:49:08 +0800 Subject: [PATCH] ARM: dtd: fp5280g2: ENable KCS 3 for MCTP binding Message-ID: <20210819054908.140514-1-liuxiwei@inspur.com> Signed-off-by: George Liu --- arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts index 1752f3250e44..d0c3acbf6c8c 100644 --- a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts +++ b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts @@ -3,6 +3,7 @@ #include "aspeed-g5.dtsi" #include #include +#include / { model = "FP5280G2 BMC"; @@ -902,4 +903,10 @@ fan at 7 { }; +&kcs3 { + status = "okay"; + aspeed,lpc-io-reg = <0xca2>; + aspeed,lpc-interrupts = <11 IRQ_TYPE_LEVEL_LOW>; +}; + #include "ibm-power9-dual.dtsi" -- 2.30.2 From liuxiwei1013 at gmail.com Thu Aug 19 15:52:27 2021 From: liuxiwei1013 at gmail.com (George Liu) Date: Thu, 19 Aug 2021 13:52:27 +0800 Subject: [PATCH] ARM: dts: fp5280g2: Enable KCS 3 for MCTP binding Message-ID: <20210819055227.140980-1-liuxiwei@inspur.com> Signed-off-by: George Liu --- arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts index 1752f3250e44..d0c3acbf6c8c 100644 --- a/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts +++ b/arch/arm/boot/dts/aspeed-bmc-inspur-fp5280g2.dts @@ -3,6 +3,7 @@ #include "aspeed-g5.dtsi" #include #include +#include / { model = "FP5280G2 BMC"; @@ -902,4 +903,10 @@ fan at 7 { }; +&kcs3 { + status = "okay"; + aspeed,lpc-io-reg = <0xca2>; + aspeed,lpc-interrupts = <11 IRQ_TYPE_LEVEL_LOW>; +}; + #include "ibm-power9-dual.dtsi" -- 2.30.2 From joel at jms.id.au Thu Aug 19 16:51:59 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 19 Aug 2021 16:21:59 +0930 Subject: [PATCH 0/4] ARM: config: aspeed: Defconfig updates Message-ID: <20210819065203.2620911-1-joel@jms.id.au> Here are some defconfig updates that we've been using in the openbmc distro kernel tree for a while now. Joel Stanley (4): ARM: config: aspeed: Enable hardened allocator feature ARM: config: aspeed: Enable KCS adapter for raw SerIO ARM: config: aspeed_g4: Enable EDAC and SPGIO ARM: config: aspeed: Regenerate defconfigs arch/arm/configs/aspeed_g4_defconfig | 16 +++++----------- arch/arm/configs/aspeed_g5_defconfig | 25 +++++++++---------------- 2 files changed, 14 insertions(+), 27 deletions(-) -- 2.32.0 From joel at jms.id.au Thu Aug 19 16:52:00 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 19 Aug 2021 16:22:00 +0930 Subject: [PATCH 1/4] ARM: config: aspeed: Enable hardened allocator feature In-Reply-To: <20210819065203.2620911-1-joel@jms.id.au> References: <20210819065203.2620911-1-joel@jms.id.au> Message-ID: <20210819065203.2620911-2-joel@jms.id.au> SLAB_FREELIST_HARDENED can protect from freelist overwrite attacks with really small overhead. It works best with the SLUB allocator, so make SLUB the default by removing SLAB=y. total used free shared buff/cache available SLAB 425596 44065.3+/-220 311099+/-3800 14864+/-3900 70432+/-3700 352767+/-3900 SLUB 425592 44225.3+/-280 313275+/-600 12132+/-3.3 68092+/-530 355295+/-280 These figures are the average memory usage from three boots of each option in qemu, running the Romulus userspace. The output is from free(1), reported in kilobytes. Signed-off-by: Joel Stanley --- arch/arm/configs/aspeed_g4_defconfig | 2 +- arch/arm/configs/aspeed_g5_defconfig | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig index a3ee647b1ebb..397c57e94743 100644 --- a/arch/arm/configs/aspeed_g4_defconfig +++ b/arch/arm/configs/aspeed_g4_defconfig @@ -18,8 +18,8 @@ CONFIG_BPF_SYSCALL=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set -CONFIG_SLAB=y CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y # CONFIG_ARCH_MULTI_V7 is not set CONFIG_ARCH_ASPEED=y CONFIG_MACH_ASPEED_G4=y diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig index 2db48438c5d2..3f81146a9c0e 100644 --- a/arch/arm/configs/aspeed_g5_defconfig +++ b/arch/arm/configs/aspeed_g5_defconfig @@ -18,8 +18,8 @@ CONFIG_BPF_SYSCALL=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set -CONFIG_SLAB=y CONFIG_SLAB_FREELIST_RANDOM=y +CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_ARCH_MULTI_V6=y CONFIG_ARCH_ASPEED=y CONFIG_MACH_ASPEED_G5=y -- 2.32.0 From joel at jms.id.au Thu Aug 19 16:52:01 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 19 Aug 2021 16:22:01 +0930 Subject: [PATCH 2/4] ARM: config: aspeed: Enable KCS adapter for raw SerIO In-Reply-To: <20210819065203.2620911-1-joel@jms.id.au> References: <20210819065203.2620911-1-joel@jms.id.au> Message-ID: <20210819065203.2620911-3-joel@jms.id.au> Raw SerIO is used by the OpenBMC debug-trigger application to take signals from the host that applications are unresponsive on the BMC. Signed-off-by: Joel Stanley --- arch/arm/configs/aspeed_g5_defconfig | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig index 3f81146a9c0e..6ba30b212bda 100644 --- a/arch/arm/configs/aspeed_g5_defconfig +++ b/arch/arm/configs/aspeed_g5_defconfig @@ -130,7 +130,7 @@ CONFIG_KEYBOARD_GPIO_POLLED=y # CONFIG_INPUT_MOUSE is not set CONFIG_INPUT_MISC=y CONFIG_INPUT_IBM_PANEL=y -# CONFIG_SERIO is not set +CONFIG_SERIO_RAW=y # CONFIG_VT is not set # CONFIG_LEGACY_PTYS is not set CONFIG_SERIAL_8250=y @@ -144,6 +144,7 @@ CONFIG_SERIAL_8250_SHARE_IRQ=y CONFIG_SERIAL_8250_DW=y CONFIG_SERIAL_OF_PLATFORM=y CONFIG_ASPEED_KCS_IPMI_BMC=y +CONFIG_IPMI_KCS_BMC_SERIO=y CONFIG_ASPEED_BT_IPMI_BMC=y CONFIG_HW_RANDOM_TIMERIOMEM=y # CONFIG_I2C_COMPAT is not set -- 2.32.0 From joel at jms.id.au Thu Aug 19 16:52:02 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 19 Aug 2021 16:22:02 +0930 Subject: [PATCH 3/4] ARM: config: aspeed_g4: Enable EDAC and SPGIO In-Reply-To: <20210819065203.2620911-1-joel@jms.id.au> References: <20210819065203.2620911-1-joel@jms.id.au> Message-ID: <20210819065203.2620911-4-joel@jms.id.au> These two devices are part of the AST2400 and the drivers have support for that version of the soc. Signed-off-by: Joel Stanley --- arch/arm/configs/aspeed_g4_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig index 397c57e94743..a578865c2d96 100644 --- a/arch/arm/configs/aspeed_g4_defconfig +++ b/arch/arm/configs/aspeed_g4_defconfig @@ -141,6 +141,7 @@ CONFIG_SPI=y CONFIG_GPIOLIB=y CONFIG_GPIO_SYSFS=y CONFIG_GPIO_ASPEED=y +CONFIG_GPIO_ASPEED_SGPIO=y CONFIG_W1=y CONFIG_W1_MASTER_GPIO=y CONFIG_W1_SLAVE_THERM=y @@ -205,6 +206,7 @@ CONFIG_LEDS_TRIGGER_TIMER=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y CONFIG_LEDS_TRIGGER_DEFAULT_ON=y CONFIG_EDAC=y +CONFIG_EDAC_ASPEED=y CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=y CONFIG_RTC_DRV_PCF8523=y -- 2.32.0 From joel at jms.id.au Thu Aug 19 16:52:03 2021 From: joel at jms.id.au (Joel Stanley) Date: Thu, 19 Aug 2021 16:22:03 +0930 Subject: [PATCH 4/4] ARM: config: aspeed: Regenerate defconfigs In-Reply-To: <20210819065203.2620911-1-joel@jms.id.au> References: <20210819065203.2620911-1-joel@jms.id.au> Message-ID: <20210819065203.2620911-5-joel@jms.id.au> A make defconfig && make savedefconfig was performed for each configuration. Most changes are due to options moving around, except for the following which are due to changing defaults: - SECCOMP is enabled by default as of commit 282a181b1a0d ("seccomp: Move config option SECCOMP to arch/Kconfig") in v5.9 - The soc drivers ASPEED_LPC_CTRL, ASPEED_LPC_SNOOP and ASPEED_P2A_CTRL are enabled by default when the aspeed platform is enabled, as of commit 592693a1f881 ("soc: aspeed: Improve kconfig") in v5.9 - The ZBOOT_ROM_TEXT/BSS values fall out of the defconfig as of commit 39c3e304567a ("ARM: 8984/1: Kconfig: set default ZBOOT_ROM_TEXT/BSS value to 0x0") in v5.8 - I2C_MUX is selected by MEDIA_SUBDRV_AUTOSELECT, probably as of about v5.8. It was in the config as it is required bt the PCA I2C muxes that enabled it Signed-off-by: Joel Stanley --- arch/arm/configs/aspeed_g4_defconfig | 12 ++---------- arch/arm/configs/aspeed_g5_defconfig | 20 ++++++-------------- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig index a578865c2d96..acaafa351d08 100644 --- a/arch/arm/configs/aspeed_g4_defconfig +++ b/arch/arm/configs/aspeed_g4_defconfig @@ -3,6 +3,7 @@ CONFIG_KERNEL_XZ=y CONFIG_SYSVIPC=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 @@ -14,7 +15,6 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_UID16 is not set # CONFIG_SYSFS_SYSCALL is not set # CONFIG_AIO is not set -CONFIG_BPF_SYSCALL=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set @@ -26,10 +26,7 @@ CONFIG_MACH_ASPEED_G4=y CONFIG_VMSPLIT_2G=y CONFIG_AEABI=y CONFIG_UACCESS_WITH_MEMCPY=y -CONFIG_SECCOMP=y # CONFIG_ATAGS is not set -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_ARM_APPENDED_DTB=y CONFIG_ARM_ATAG_DTB_COMPAT=y CONFIG_KEXEC=y @@ -76,7 +73,6 @@ CONFIG_NETCONSOLE=y # CONFIG_NET_VENDOR_AMAZON is not set # CONFIG_NET_VENDOR_AQUANTIA is not set # CONFIG_NET_VENDOR_ARC is not set -# CONFIG_NET_VENDOR_AURORA is not set # CONFIG_NET_VENDOR_BROADCOM is not set # CONFIG_NET_VENDOR_CADENCE is not set # CONFIG_NET_VENDOR_CAVIUM is not set @@ -132,7 +128,6 @@ CONFIG_ASPEED_BT_IPMI_BMC=y CONFIG_HW_RANDOM_TIMERIOMEM=y # CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y -CONFIG_I2C_MUX=y CONFIG_I2C_MUX_PCA9541=y CONFIG_I2C_MUX_PCA954x=y CONFIG_I2C_ASPEED=y @@ -214,9 +209,6 @@ CONFIG_RTC_DRV_RV8803=y CONFIG_RTC_DRV_ASPEED=y # CONFIG_VIRTIO_MENU is not set # CONFIG_IOMMU_SUPPORT is not set -CONFIG_ASPEED_LPC_CTRL=y -CONFIG_ASPEED_LPC_SNOOP=y -CONFIG_ASPEED_P2A_CTRL=y CONFIG_IIO=y CONFIG_ASPEED_ADC=y CONFIG_MAX1363=y @@ -257,6 +249,7 @@ CONFIG_DEBUG_INFO_DWARF4=y CONFIG_GDB_SCRIPTS=y CONFIG_STRIP_ASM_SYMS=y CONFIG_DEBUG_FS=y +CONFIG_DEBUG_WX=y CONFIG_SCHED_STACK_END_CHECK=y CONFIG_PANIC_ON_OOPS=y CONFIG_PANIC_TIMEOUT=-1 @@ -265,6 +258,5 @@ CONFIG_SOFTLOCKUP_DETECTOR=y CONFIG_WQ_WATCHDOG=y # CONFIG_SCHED_DEBUG is not set CONFIG_FUNCTION_TRACER=y -CONFIG_DEBUG_WX=y CONFIG_DEBUG_USER=y # CONFIG_RUNTIME_TESTING_MENU is not set diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig index 6ba30b212bda..480dbbb4ff91 100644 --- a/arch/arm/configs/aspeed_g5_defconfig +++ b/arch/arm/configs/aspeed_g5_defconfig @@ -3,6 +3,7 @@ CONFIG_KERNEL_XZ=y CONFIG_SYSVIPC=y CONFIG_NO_HZ_IDLE=y CONFIG_HIGH_RES_TIMERS=y +CONFIG_BPF_SYSCALL=y CONFIG_IKCONFIG=y CONFIG_IKCONFIG_PROC=y CONFIG_LOG_BUF_SHIFT=16 @@ -14,7 +15,6 @@ CONFIG_BLK_DEV_INITRD=y # CONFIG_UID16 is not set # CONFIG_SYSFS_SYSCALL is not set # CONFIG_AIO is not set -CONFIG_BPF_SYSCALL=y CONFIG_EMBEDDED=y CONFIG_PERF_EVENTS=y # CONFIG_COMPAT_BRK is not set @@ -31,10 +31,7 @@ CONFIG_VMSPLIT_2G=y CONFIG_NR_CPUS=2 CONFIG_HIGHMEM=y CONFIG_UACCESS_WITH_MEMCPY=y -CONFIG_SECCOMP=y # CONFIG_ATAGS is not set -CONFIG_ZBOOT_ROM_TEXT=0x0 -CONFIG_ZBOOT_ROM_BSS=0x0 CONFIG_KEXEC=y CONFIG_VFP=y CONFIG_NEON=y @@ -89,7 +86,6 @@ CONFIG_NETCONSOLE=y # CONFIG_NET_VENDOR_AMAZON is not set # CONFIG_NET_VENDOR_AQUANTIA is not set # CONFIG_NET_VENDOR_ARC is not set -# CONFIG_NET_VENDOR_AURORA is not set # CONFIG_NET_VENDOR_BROADCOM is not set # CONFIG_NET_VENDOR_CADENCE is not set # CONFIG_NET_VENDOR_CAVIUM is not set @@ -149,7 +145,6 @@ CONFIG_ASPEED_BT_IPMI_BMC=y CONFIG_HW_RANDOM_TIMERIOMEM=y # CONFIG_I2C_COMPAT is not set CONFIG_I2C_CHARDEV=y -CONFIG_I2C_MUX=y CONFIG_I2C_MUX_GPIO=y CONFIG_I2C_MUX_PCA9541=y CONFIG_I2C_MUX_PCA954x=y @@ -232,9 +227,6 @@ CONFIG_RTC_DRV_RV8803=y CONFIG_RTC_DRV_ASPEED=y # CONFIG_VIRTIO_MENU is not set # CONFIG_IOMMU_SUPPORT is not set -CONFIG_ASPEED_LPC_CTRL=y -CONFIG_ASPEED_LPC_SNOOP=y -CONFIG_ASPEED_P2A_CTRL=y CONFIG_IIO=y CONFIG_ASPEED_ADC=y CONFIG_MAX1363=y @@ -279,14 +271,14 @@ CONFIG_DEBUG_INFO_DWARF4=y CONFIG_GDB_SCRIPTS=y CONFIG_STRIP_ASM_SYMS=y CONFIG_DEBUG_FS=y +CONFIG_DEBUG_WX=y +CONFIG_SCHED_STACK_END_CHECK=y +CONFIG_PANIC_ON_OOPS=y +CONFIG_PANIC_TIMEOUT=-1 CONFIG_SOFTLOCKUP_DETECTOR=y # CONFIG_DETECT_HUNG_TASK is not set CONFIG_WQ_WATCHDOG=y -CONFIG_PANIC_ON_OOPS=y -CONFIG_PANIC_TIMEOUT=-1 # CONFIG_SCHED_DEBUG is not set -CONFIG_SCHED_STACK_END_CHECK=y CONFIG_FUNCTION_TRACER=y -# CONFIG_RUNTIME_TESTING_MENU is not set -CONFIG_DEBUG_WX=y CONFIG_DEBUG_USER=y +# CONFIG_RUNTIME_TESTING_MENU is not set -- 2.32.0 From andrew at aj.id.au Thu Aug 19 16:58:32 2021 From: andrew at aj.id.au (Andrew Jeffery) Date: Thu, 19 Aug 2021 16:28:32 +0930 Subject: [PATCH 0/4] ARM: config: aspeed: Defconfig updates In-Reply-To: <20210819065203.2620911-1-joel@jms.id.au> References: <20210819065203.2620911-1-joel@jms.id.au> Message-ID: On Thu, 19 Aug 2021, at 16:21, Joel Stanley wrote: > Here are some defconfig updates that we've been using in the openbmc > distro kernel tree for a while now. > > Joel Stanley (4): > ARM: config: aspeed: Enable hardened allocator feature > ARM: config: aspeed: Enable KCS adapter for raw SerIO > ARM: config: aspeed_g4: Enable EDAC and SPGIO > ARM: config: aspeed: Regenerate defconfigs > > arch/arm/configs/aspeed_g4_defconfig | 16 +++++----------- > arch/arm/configs/aspeed_g5_defconfig | 25 +++++++++---------------- > 2 files changed, 14 insertions(+), 27 deletions(-) For the series: Reviewed-by: Andrew Jeffery From chiawei_wang at aspeedtech.com Thu Aug 19 18:00:37 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 19 Aug 2021 16:00:37 +0800 Subject: [PATCH v2 2/5] MAINTAINER: Add ASPEED eSPI driver entry In-Reply-To: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> Message-ID: <20210819080040.31242-3-chiawei_wang@aspeedtech.com> Add Ryan Chen and myself as the maintainer of Aspeed eSPI driver. Joel Stanley is added as the reviewer. Signed-off-by: Chia-Wei Wang --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fd25e4ecf0b9..b21bcb46692e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1713,6 +1713,15 @@ F: drivers/crypto/axis F: drivers/mmc/host/usdhi6rol0.c F: drivers/pinctrl/pinctrl-artpec* +ARM/ASPEED ESPI DRIVER +M: Chia-Wei Wang +M: Ryan Chen +R: Joel Stanley +L: linux-aspeed at lists.ozlabs.org (moderated for non-subscribers) +L: openbmc at lists.ozlabs.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/soc/aspeed/espi.yaml + ARM/ASPEED I2C DRIVER M: Brendan Higgins R: Benjamin Herrenschmidt -- 2.17.1 From chiawei_wang at aspeedtech.com Thu Aug 19 18:00:36 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 19 Aug 2021 16:00:36 +0800 Subject: [PATCH v2 1/5] dt-bindings: aspeed: Add eSPI controller In-Reply-To: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> Message-ID: <20210819080040.31242-2-chiawei_wang@aspeedtech.com> Add dt-bindings for Aspeed eSPI controller Signed-off-by: Chia-Wei Wang --- .../devicetree/bindings/soc/aspeed/espi.yaml | 158 ++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml diff --git a/Documentation/devicetree/bindings/soc/aspeed/espi.yaml b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml new file mode 100644 index 000000000000..fec3d37f3ffd --- /dev/null +++ b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml @@ -0,0 +1,158 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# # Copyright (c) 2021 Aspeed Technology Inc. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/aspeed/espi.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Aspeed eSPI Controller + +maintainers: + - Chia-Wei Wang + - Ryan Chen + +description: + Aspeed eSPI controller implements a slave side eSPI endpoint device + supporting the four eSPI channels, namely peripheral, virtual wire, + out-of-band, and flash. + +properties: + compatible: + items: + - enum: + - aspeed,ast2500-espi + - aspeed,ast2600-espi + - const: simple-mfd + - const: syscon + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + + espi-ctrl: + type: object + + properties: + compatible: + items: + - enum: + - aspeed,ast2500-espi-ctrl + - aspeed,ast2600-espi-ctrl + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + perif,memcyc-enable: + type: boolean + description: Enable memory cycle over eSPI peripheral channel + + perif,memcyc-src-addr: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: The Host side address to be decoded into the memory cycle over eSPI peripheral channel + + perif,memcyc-size: + $ref: "/schemas/types.yaml#/definitions/uint32" + description: The size of the memory region allocated for the memory cycle over eSPI peripheral channel + minimum: 65536 + + perif,dma-mode: + type: boolean + description: Enable DMA support for eSPI peripheral channel + + oob,dma-mode: + type: boolean + description: Enable DMA support for eSPI out-of-band channel + + oob,dma-tx-desc-num: + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 2 + maximum: 1023 + description: The number of TX descriptors available for eSPI OOB DMA engine + + oob,dma-rx-desc-num: + $ref: "/schemas/types.yaml#/definitions/uint32" + minimum: 2 + maximum: 1023 + description: The number of RX descriptors available for eSPI OOB DMA engine + + flash,dma-mode: + type: boolean + description: Enable DMA support for eSPI flash channel + + flash,safs-mode: + $ref: "/schemas/types.yaml#/definitions/uint32" + enum: [ 0, 1, 2 ] + default: 0 + description: Slave-Attached-Sharing-Flash mode, 0->Mix, 1->SW, 2->HW + + dependencies: + perif,memcyc-src-addr: [ perif,memcyc-enable ] + perif,memcyc-size: [ perif,memcyc-enable ] + oob,dma-tx-desc-num: [ oob,dma-mode ] + oob,dma-rx-desc-num: [ oob,dma-mode ] + + required: + - compatible + - interrupts + - clocks + + espi-mmbi: + type: object + + properties: + compatible: + const: aspeed, ast2600-espi-mmbi + + interrupts: + maxItems: 1 + + required: + - compatible + - interrupts + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + #include + + espi: espi at 1e6ee000 { + compatible = "aspeed,ast2600-espi", "simple-mfd", "syscon"; + reg = <0x1e6ee000 0x1000>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x1e6ee000 0x1000>; + + espi_ctrl: espi-ctrl at 0 { + compatible = "aspeed,ast2600-espi-ctrl"; + reg = <0x0 0x800>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; + resets = <&syscon ASPEED_RESET_ESPI>; + }; + + espi_mmbi: espi-flash-channel at 800 { + compatible = "aspeed,ast2600-espi-flash"; + reg = <0x800 0x50>; + interrupts = ; + }; + }; -- 2.17.1 From chiawei_wang at aspeedtech.com Thu Aug 19 18:00:35 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 19 Aug 2021 16:00:35 +0800 Subject: [PATCH v2 0/5] arm: aspeed: Add eSPI support Message-ID: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> This patch series add the driver support for the eSPI controller of Aspeed 5/6th generation SoCs. This controller is a slave device communicating with a master over Enhanced Serial Peripheral Interface (eSPI). It supports all of the 4 eSPI channels, namely peripheral, virtual wire, out-of-band, and flash, and operates at max frequency of 66MHz. v2: - remove irqchip implementation - merge per-channel drivers into single one to avoid the racing issue among eSPI handshake process and driver probing. Chia-Wei Wang (5): dt-bindings: aspeed: Add eSPI controller MAINTAINER: Add ASPEED eSPI driver entry clk: aspeed: Add eSPI reset bit soc: aspeed: Add eSPI driver ARM: dts: aspeed: Add eSPI node .../devicetree/bindings/soc/aspeed/espi.yaml | 158 +++++ MAINTAINERS | 9 + arch/arm/boot/dts/aspeed-g6.dtsi | 17 + drivers/soc/aspeed/Kconfig | 11 + drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-espi-ctrl.c | 206 ++++++ drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 +++++++++ drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++ drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++ drivers/soc/aspeed/aspeed-espi-oob.h | 611 ++++++++++++++++++ drivers/soc/aspeed/aspeed-espi-perif.h | 512 +++++++++++++++ drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++ include/dt-bindings/clock/ast2600-clock.h | 1 + 13 files changed, 2505 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.h create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.h create mode 100644 drivers/soc/aspeed/aspeed-espi-ioc.h create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.h create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.h create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.h -- 2.17.1 From chiawei_wang at aspeedtech.com Thu Aug 19 18:00:38 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 19 Aug 2021 16:00:38 +0800 Subject: [PATCH v2 3/5] clk: aspeed: Add eSPI reset bit In-Reply-To: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> Message-ID: <20210819080040.31242-4-chiawei_wang@aspeedtech.com> Add bit field definition for the eSPI reset control Signed-off-by: Chia-Wei Wang --- include/dt-bindings/clock/ast2600-clock.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dt-bindings/clock/ast2600-clock.h b/include/dt-bindings/clock/ast2600-clock.h index 62b9520a00fd..964934b1caef 100644 --- a/include/dt-bindings/clock/ast2600-clock.h +++ b/include/dt-bindings/clock/ast2600-clock.h @@ -89,6 +89,7 @@ #define ASPEED_CLK_MAC4RCLK 70 /* Only list resets here that are not part of a gate */ +#define ASPEED_RESET_ESPI 57 #define ASPEED_RESET_ADC 55 #define ASPEED_RESET_JTAG_MASTER2 54 #define ASPEED_RESET_I3C_DMA 39 -- 2.17.1 From chiawei_wang at aspeedtech.com Thu Aug 19 18:00:40 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 19 Aug 2021 16:00:40 +0800 Subject: [PATCH v2 5/5] ARM: dts: aspeed: Add eSPI node In-Reply-To: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> Message-ID: <20210819080040.31242-6-chiawei_wang@aspeedtech.com> Add eSPI to the device tree for Aspeed 5/6th generation SoCs. Signed-off-by: Chia-Wei Wang --- arch/arm/boot/dts/aspeed-g6.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index f96607b7b4e2..47dc0b3993d1 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -364,6 +364,23 @@ status = "disabled"; }; + espi: espi at 1e6ee000 { + compatible = "aspeed,ast2600-espi", "simple-mfd", "syscon"; + reg = <0x1e6ee000 0x1000>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x1e6ee000 0x1000>; + + espi_ctrl: espi-ctrl at 0 { + compatible = "aspeed,ast2600-espi-ctrl"; + reg = <0x0 0x800>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; + status = "disabled"; + }; + }; + gpio0: gpio at 1e780000 { #gpio-cells = <2>; gpio-controller; -- 2.17.1 From robh at kernel.org Thu Aug 19 22:49:09 2021 From: robh at kernel.org (Rob Herring) Date: Thu, 19 Aug 2021 07:49:09 -0500 Subject: [PATCH v2 1/5] dt-bindings: aspeed: Add eSPI controller In-Reply-To: <20210819080040.31242-2-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> <20210819080040.31242-2-chiawei_wang@aspeedtech.com> Message-ID: <1629377349.157361.288210.nullmailer@robh.at.kernel.org> On Thu, 19 Aug 2021 16:00:36 +0800, Chia-Wei Wang wrote: > Add dt-bindings for Aspeed eSPI controller > > Signed-off-by: Chia-Wei Wang > --- > .../devicetree/bindings/soc/aspeed/espi.yaml | 158 ++++++++++++++++++ > 1 file changed, 158 insertions(+) > create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml > My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13): yamllint warnings/errors: dtschema/dtc warnings/errors: Error: Documentation/devicetree/bindings/soc/aspeed/espi.example.dts:35.35-36 syntax error FATAL ERROR: Unable to parse input tree make[1]: *** [scripts/Makefile.lib:380: Documentation/devicetree/bindings/soc/aspeed/espi.example.dt.yaml] Error 1 make[1]: *** Waiting for unfinished jobs.... make: *** [Makefile:1419: dt_binding_check] Error 2 doc reference errors (make refcheckdocs): See https://patchwork.ozlabs.org/patch/1518493 This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit. From joel at jms.id.au Fri Aug 20 16:57:24 2021 From: joel at jms.id.au (Joel Stanley) Date: Fri, 20 Aug 2021 06:57:24 +0000 Subject: [GIT PULL] ARM: aspeed: defconfig changes for 5.15 Message-ID: Dear Soc maintainers, This is the last of the aspeed pull requests for v5.15. Apologies for drip feeding them, I was pressed for time so I started with the most important and worked down the list. The following changes since commit 2734d6c1b1a089fb593ef6a23d4b70903526fe0c: Linux 5.14-rc2 (2021-07-18 14:13:49 -0700) are available in the Git repository at: git://git.kernel.org/pub/scm/linux/kernel/git/joel/bmc.git tags/aspeed-5.15-defconfig for you to fetch changes up to a4836d5ad127fe31fecb29878d81f2e9b4a5591c: ARM: config: aspeed: Regenerate defconfigs (2021-08-19 16:33:43 +0930) ---------------------------------------------------------------- ASPEED defconfig updates for 5.15 - Enable new KCS SerIO driver - Enable SGPIO and EDAC for AST2400 now they are supported there - Switch to SLUB and enable SLAB_FREELIST_HARDENED - Regenerate defconfigs atop v5.14-rc2 ---------------------------------------------------------------- Joel Stanley (4): ARM: config: aspeed: Enable hardened allocator feature ARM: config: aspeed: Enable KCS adapter for raw SerIO ARM: config: aspeed_g4: Enable EDAC and SPGIO ARM: config: aspeed: Regenerate defconfigs arch/arm/configs/aspeed_g4_defconfig | 16 +++++----------- arch/arm/configs/aspeed_g5_defconfig | 25 +++++++++---------------- 2 files changed, 14 insertions(+), 27 deletions(-) From robh at kernel.org Sat Aug 21 06:01:55 2021 From: robh at kernel.org (Rob Herring) Date: Fri, 20 Aug 2021 15:01:55 -0500 Subject: [PATCH v2 1/5] dt-bindings: aspeed: Add eSPI controller In-Reply-To: <20210819080040.31242-2-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> <20210819080040.31242-2-chiawei_wang@aspeedtech.com> Message-ID: On Thu, Aug 19, 2021 at 04:00:36PM +0800, Chia-Wei Wang wrote: > Add dt-bindings for Aspeed eSPI controller > > Signed-off-by: Chia-Wei Wang > --- > .../devicetree/bindings/soc/aspeed/espi.yaml | 158 ++++++++++++++++++ > 1 file changed, 158 insertions(+) > create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml > > diff --git a/Documentation/devicetree/bindings/soc/aspeed/espi.yaml b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml > new file mode 100644 > index 000000000000..fec3d37f3ffd > --- /dev/null > +++ b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml > @@ -0,0 +1,158 @@ > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) > +# # Copyright (c) 2021 Aspeed Technology Inc. > +%YAML 1.2 > +--- > +$id: "http://devicetree.org/schemas/soc/aspeed/espi.yaml#" > +$schema: "http://devicetree.org/meta-schemas/core.yaml#" > + > +title: Aspeed eSPI Controller > + > +maintainers: > + - Chia-Wei Wang > + - Ryan Chen > + > +description: > + Aspeed eSPI controller implements a slave side eSPI endpoint device > + supporting the four eSPI channels, namely peripheral, virtual wire, > + out-of-band, and flash. > + > +properties: > + compatible: > + items: > + - enum: > + - aspeed,ast2500-espi > + - aspeed,ast2600-espi > + - const: simple-mfd > + - const: syscon Is this really 2 sub devices that could be used individually or in a different combination? If not, then I'd make all this 1 node. Rob From chiawei_wang at aspeedtech.com Thu Aug 19 18:00:39 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 19 Aug 2021 16:00:39 +0800 Subject: [PATCH v2 4/5] soc: aspeed: Add eSPI driver In-Reply-To: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> Message-ID: <20210819080040.31242-5-chiawei_wang@aspeedtech.com> The Aspeed eSPI controller is slave device to communicate with the master through the Enhanced Serial Peripheral Interface (eSPI). All of the four eSPI channels, namely peripheral, virtual wire, out-of-band, and flash are supported. Signed-off-by: Chia-Wei Wang --- drivers/soc/aspeed/Kconfig | 11 + drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-espi-ctrl.c | 206 +++++++++ drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 ++++++++++++ drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++ drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++++ drivers/soc/aspeed/aspeed-espi-oob.h | 611 +++++++++++++++++++++++++ drivers/soc/aspeed/aspeed-espi-perif.h | 512 +++++++++++++++++++++ drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++++ 9 files changed, 2320 insertions(+) create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.h create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.h create mode 100644 drivers/soc/aspeed/aspeed-espi-ioc.h create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.h create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.h create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.h diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index 243ca196e6ad..7617a02df5cf 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -42,6 +42,17 @@ config ASPEED_SOCINFO help Say yes to support decoding of ASPEED BMC information. +config ASPEED_ESPI + bool "ASPEED eSPI slave driver" + select REGMAP + select MFD_SYSCON + default n + help + Enable driver support for the Aspeed eSPI engine. The eSPI engine + plays as a slave device in BMC to communicate with the Host over + the eSPI interface. The four eSPI channels, namely peripheral, + virtual wire, out-of-band, and flash are supported. + endmenu endif diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index fcab7192e1a4..650d8de2875d 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o +obj-$(CONFIG_ASPEED_ESPI) += aspeed-espi-ctrl.o diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.c b/drivers/soc/aspeed/aspeed-espi-ctrl.c new file mode 100644 index 000000000000..80a135d357f1 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-ctrl.c @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aspeed-espi-ioc.h" +#include "aspeed-espi-ctrl.h" + +/* include alloc/free/event/ioctl handlers of the eSPI 4 channels */ +#include "aspeed-espi-perif.h" +#include "aspeed-espi-vw.h" +#include "aspeed-espi-oob.h" +#include "aspeed-espi-flash.h" + +#define DEVICE_NAME "aspeed-espi-ctrl" + +static irqreturn_t aspeed_espi_ctrl_isr(int irq, void *arg) +{ + uint32_t sts; + struct aspeed_espi_ctrl *espi_ctrl = (struct aspeed_espi_ctrl *)arg; + + regmap_read(espi_ctrl->map, ESPI_INT_STS, &sts); + + if (sts & ESPI_INT_STS_PERIF_BITS) { + aspeed_espi_perif_event(sts, espi_ctrl->perif); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_PERIF_BITS); + } + + if (sts & ESPI_INT_STS_VW_BITS) { + aspeed_espi_vw_event(sts, espi_ctrl->vw); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_VW_BITS); + } + + if (sts & (ESPI_INT_STS_OOB_BITS)) { + aspeed_espi_oob_event(sts, espi_ctrl->oob); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_OOB_BITS); + } + + if (sts & ESPI_INT_STS_FLASH_BITS) { + aspeed_espi_flash_event(sts, espi_ctrl->flash); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_FLASH_BITS); + } + + if (sts & ESPI_INT_STS_HW_RST_DEASSERT) { + aspeed_espi_perif_enable(espi_ctrl->perif); + aspeed_espi_vw_enable(espi_ctrl->vw); + aspeed_espi_oob_enable(espi_ctrl->oob); + aspeed_espi_flash_enable(espi_ctrl->flash); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1); + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1); + + if (espi_ctrl->version == ESPI_AST2500) + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T2, + ESPI_SYSEVT_INT_T2_HOST_RST_WARN | + ESPI_SYSEVT_INT_T2_OOB_RST_WARN); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_HW_RST_DEASSERT, + ESPI_INT_EN_HW_RST_DEASSERT); + + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT, + ESPI_SYSEVT_SLV_BOOT_STS | ESPI_SYSEVT_SLV_BOOT_DONE, + ESPI_SYSEVT_SLV_BOOT_STS | ESPI_SYSEVT_SLV_BOOT_DONE); + + regmap_write(espi_ctrl->map, ESPI_INT_STS, ESPI_INT_STS_HW_RST_DEASSERT); + } + + return IRQ_HANDLED; +} + +static int aspeed_espi_ctrl_probe(struct platform_device *pdev) +{ + int rc = 0; + uint32_t reg; + struct aspeed_espi_ctrl *espi_ctrl; + struct device *dev = &pdev->dev; + struct regmap *scu; + + espi_ctrl = devm_kzalloc(dev, sizeof(*espi_ctrl), GFP_KERNEL); + if (!espi_ctrl) + return -ENOMEM; + + espi_ctrl->version = (uint32_t)of_device_get_match_data(dev); + + espi_ctrl->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(espi_ctrl->map)) { + dev_err(dev, "cannot get remap\n"); + return -ENODEV; + } + + espi_ctrl->irq = platform_get_irq(pdev, 0); + if (espi_ctrl->irq < 0) + return espi_ctrl->irq; + + espi_ctrl->clk = devm_clk_get(dev, NULL); + if (IS_ERR(espi_ctrl->clk)) { + dev_err(dev, "cannot get clock\n"); + return -ENODEV; + } + + rc = clk_prepare_enable(espi_ctrl->clk); + if (rc) { + dev_err(dev, "cannot enable clock\n"); + return rc; + } + + espi_ctrl->perif = aspeed_espi_perif_alloc(dev, espi_ctrl); + if (IS_ERR(espi_ctrl->perif)) { + dev_err(dev, "failed to allocate peripheral channel\n"); + return PTR_ERR(espi_ctrl->perif); + } + + espi_ctrl->vw = aspeed_espi_vw_alloc(dev, espi_ctrl); + if (IS_ERR(espi_ctrl->vw)) { + dev_err(dev, "failed to allocate virtual wire channel\n"); + return PTR_ERR(espi_ctrl->vw); + } + + espi_ctrl->oob = aspeed_espi_oob_alloc(dev, espi_ctrl); + if (IS_ERR(espi_ctrl->oob)) { + dev_err(dev, "failed to allocate out-of-band channel\n"); + return PTR_ERR(espi_ctrl->oob); + } + + espi_ctrl->flash = aspeed_espi_flash_alloc(dev, espi_ctrl); + if (rc) { + dev_err(dev, "failed to allocate flash channel\n"); + return PTR_ERR(espi_ctrl->flash); + } + + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1); + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1); + + rc = devm_request_irq(dev, espi_ctrl->irq, + aspeed_espi_ctrl_isr, + 0, DEVICE_NAME, espi_ctrl); + if (rc) { + dev_err(dev, "failed to request IRQ\n"); + return rc; + } + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_HW_RST_DEASSERT, + ESPI_INT_EN_HW_RST_DEASSERT); + + dev_set_drvdata(dev, espi_ctrl); + + dev_info(dev, "module loaded\n"); + + return 0; +} + +static int aspeed_espi_ctrl_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aspeed_espi_ctrl *espi_ctrl = dev_get_drvdata(dev); + + aspeed_espi_perif_free(dev, espi_ctrl->perif); + aspeed_espi_vw_free(dev, espi_ctrl->vw); + aspeed_espi_oob_free(dev, espi_ctrl->oob); + aspeed_espi_flash_free(dev, espi_ctrl->flash); + + return 0; +} + +static const struct of_device_id aspeed_espi_ctrl_of_matches[] = { + { .compatible = "aspeed,ast2500-espi-ctrl", .data = (void *)ESPI_AST2500 }, + { .compatible = "aspeed,ast2600-espi-ctrl", .data = (void *)ESPI_AST2600 }, + { }, +}; + +static struct platform_driver aspeed_espi_ctrl_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_espi_ctrl_of_matches, + }, + .probe = aspeed_espi_ctrl_probe, + .remove = aspeed_espi_ctrl_remove, +}; + +module_platform_driver(aspeed_espi_ctrl_driver); + +MODULE_AUTHOR("Chia-Wei Wang "); +MODULE_AUTHOR("Ryan Chen "); +MODULE_DESCRIPTION("Control of Aspeed eSPI Slave Device"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.h b/drivers/soc/aspeed/aspeed-espi-ctrl.h new file mode 100644 index 000000000000..44b93698fd0f --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-ctrl.h @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#ifndef _ASPEED_ESPI_CTRL_H_ +#define _ASPEED_ESPI_CTRL_H_ + +#include + +enum aspeed_espi_version { + ESPI_AST2500, + ESPI_AST2600, +}; + +struct aspeed_espi_ctrl { + struct device *dev; + + struct regmap *map; + struct clk *clk; + + int irq; + + struct aspeed_espi_perif *perif; + struct aspeed_espi_vw *vw; + struct aspeed_espi_oob *oob; + struct aspeed_espi_flash *flash; + + uint32_t version; +}; + +/* eSPI register offset */ +#define ESPI_CTRL 0x000 +#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) +#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) +#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) +#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) +#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) +#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) +#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) +#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) +#define ESPI_CTRL_FLASH_SW_MODE_MASK GENMASK(11, 10) +#define ESPI_CTRL_FLASH_SW_MODE_SHIFT 10 +#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) +#define ESPI_CTRL_FLASH_SW_RDY BIT(7) +#define ESPI_CTRL_OOB_SW_RDY BIT(4) +#define ESPI_CTRL_VW_SW_RDY BIT(3) +#define ESPI_CTRL_PERIF_SW_RDY BIT(1) +#define ESPI_STS 0x004 +#define ESPI_INT_STS 0x008 +#define ESPI_INT_STS_HW_RST_DEASSERT BIT(31) +#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) +#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) +#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) +#define ESPI_INT_STS_OOB_TX_ERR BIT(20) +#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) +#define ESPI_INT_STS_OOB_TX_ABT BIT(18) +#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) +#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) +#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) +#define ESPI_INT_STS_OOB_RX_ABT BIT(14) +#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) +#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) +#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) +#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) +#define ESPI_INT_STS_VW_GPIOEVT BIT(9) +#define ESPI_INT_STS_VW_SYSEVT BIT(8) +#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) +#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) +#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) +#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) +#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) +#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) +#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) +#define ESPI_INT_EN 0x00c +#define ESPI_INT_EN_HW_RST_DEASSERT BIT(31) +#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) +#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) +#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) +#define ESPI_INT_EN_OOB_TX_ERR BIT(20) +#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) +#define ESPI_INT_EN_OOB_TX_ABT BIT(18) +#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) +#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) +#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) +#define ESPI_INT_EN_OOB_RX_ABT BIT(14) +#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) +#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) +#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) +#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) +#define ESPI_INT_EN_VW_GPIOEVT BIT(9) +#define ESPI_INT_EN_VW_SYSEVT BIT(8) +#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) +#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) +#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) +#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) +#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) +#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) +#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) +#define ESPI_PERIF_PC_RX_DMA 0x010 +#define ESPI_PERIF_PC_RX_CTRL 0x014 +#define ESPI_PERIF_PC_RX_CTRL_PEND_SERV BIT(31) +#define ESPI_PERIF_PC_RX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT 12 +#define ESPI_PERIF_PC_RX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT 8 +#define ESPI_PERIF_PC_RX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT 0 +#define ESPI_PERIF_PC_RX_PORT 0x018 +#define ESPI_PERIF_PC_TX_DMA 0x020 +#define ESPI_PERIF_PC_TX_CTRL 0x024 +#define ESPI_PERIF_PC_TX_CTRL_TRIGGER BIT(31) +#define ESPI_PERIF_PC_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_PERIF_PC_TX_CTRL_LEN_SHIFT 12 +#define ESPI_PERIF_PC_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_PERIF_PC_TX_CTRL_TAG_SHIFT 8 +#define ESPI_PERIF_PC_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_PERIF_PC_TX_CTRL_CYC_SHIFT 0 +#define ESPI_PERIF_PC_TX_PORT 0x028 +#define ESPI_PERIF_NP_TX_DMA 0x030 +#define ESPI_PERIF_NP_TX_CTRL 0x034 +#define ESPI_PERIF_NP_TX_CTRL_TRIGGER BIT(31) +#define ESPI_PERIF_NP_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_PERIF_NP_TX_CTRL_LEN_SHIFT 12 +#define ESPI_PERIF_NP_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_PERIF_NP_TX_CTRL_TAG_SHIFT 8 +#define ESPI_PERIF_NP_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_PERIF_NP_TX_CTRL_CYC_SHIFT 0 +#define ESPI_PERIF_NP_TX_PORT 0x038 +#define ESPI_OOB_RX_DMA 0x040 +#define ESPI_OOB_RX_CTRL 0x044 +#define ESPI_OOB_RX_CTRL_PEND_SERV BIT(31) +#define ESPI_OOB_RX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_OOB_RX_CTRL_LEN_SHIFT 12 +#define ESPI_OOB_RX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_OOB_RX_CTRL_TAG_SHIFT 8 +#define ESPI_OOB_RX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_OOB_RX_CTRL_CYC_SHIFT 0 +#define ESPI_OOB_RX_PORT 0x048 +#define ESPI_OOB_TX_DMA 0x050 +#define ESPI_OOB_TX_CTRL 0x054 +#define ESPI_OOB_TX_CTRL_TRIGGER BIT(31) +#define ESPI_OOB_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_OOB_TX_CTRL_LEN_SHIFT 12 +#define ESPI_OOB_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_OOB_TX_CTRL_TAG_SHIFT 8 +#define ESPI_OOB_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_OOB_TX_CTRL_CYC_SHIFT 0 +#define ESPI_OOB_TX_PORT 0x058 +#define ESPI_FLASH_RX_DMA 0x060 +#define ESPI_FLASH_RX_CTRL 0x064 +#define ESPI_FLASH_RX_CTRL_PEND_SERV BIT(31) +#define ESPI_FLASH_RX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_FLASH_RX_CTRL_LEN_SHIFT 12 +#define ESPI_FLASH_RX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_FLASH_RX_CTRL_TAG_SHIFT 8 +#define ESPI_FLASH_RX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_FLASH_RX_CTRL_CYC_SHIFT 0 +#define ESPI_FLASH_RX_PORT 0x068 +#define ESPI_FLASH_TX_DMA 0x070 +#define ESPI_FLASH_TX_CTRL 0x074 +#define ESPI_FLASH_TX_CTRL_TRIGGER BIT(31) +#define ESPI_FLASH_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_FLASH_TX_CTRL_LEN_SHIFT 12 +#define ESPI_FLASH_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_FLASH_TX_CTRL_TAG_SHIFT 8 +#define ESPI_FLASH_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_FLASH_TX_CTRL_CYC_SHIFT 0 +#define ESPI_FLASH_TX_PORT 0x078 +#define ESPI_CTRL2 0x080 +#define ESPI_CTRL2_MEMCYC_RD_DIS BIT(6) +#define ESPI_CTRL2_MEMCYC_WR_DIS BIT(4) +#define ESPI_PERIF_PC_RX_SADDR 0x084 +#define ESPI_PERIF_PC_RX_TADDR 0x088 +#define ESPI_PERIF_PC_RX_MASK 0x08c +#define ESPI_PERIF_PC_RX_MASK_CFG_WP BIT(0) +#define ESPI_SYSEVT_INT_EN 0x094 +#define ESPI_SYSEVT 0x098 +#define ESPI_SYSEVT_HOST_RST_ACK BIT(27) +#define ESPI_SYSEVT_RST_CPU_INIT BIT(26) +#define ESPI_SYSEVT_SLV_BOOT_STS BIT(23) +#define ESPI_SYSEVT_NON_FATAL_ERR BIT(22) +#define ESPI_SYSEVT_FATAL_ERR BIT(21) +#define ESPI_SYSEVT_SLV_BOOT_DONE BIT(20) +#define ESPI_SYSEVT_OOB_RST_ACK BIT(16) +#define ESPI_SYSEVT_NMI_OUT BIT(10) +#define ESPI_SYSEVT_SMI_OUT BIT(9) +#define ESPI_SYSEVT_HOST_RST_WARN BIT(8) +#define ESPI_SYSEVT_OOB_RST_WARN BIT(6) +#define ESPI_SYSEVT_PLTRSTN BIT(5) +#define ESPI_SYSEVT_SUSPEND BIT(4) +#define ESPI_SYSEVT_S5_SLEEP BIT(2) +#define ESPI_SYSEVT_S4_SLEEP BIT(1) +#define ESPI_SYSEVT_S3_SLEEP BIT(0) +#define ESPI_VW_GPIO_VAL 0x09c +#define ESPI_GEN_CAP_N_CONF 0x0a0 +#define ESPI_CH0_CAP_N_CONF 0x0a4 +#define ESPI_CH1_CAP_N_CONF 0x0a8 +#define ESPI_CH2_CAP_N_CONF 0x0ac +#define ESPI_CH3_CAP_N_CONF 0x0b0 +#define ESPI_CH3_CAP_N_CONF2 0x0b4 +#define ESPI_SYSEVT1_INT_EN 0x100 +#define ESPI_SYSEVT1 0x104 +#define ESPI_SYSEVT1_SUSPEND_ACK BIT(20) +#define ESPI_SYSEVT1_SUSPEND_WARN BIT(0) +#define ESPI_SYSEVT_INT_T0 0x110 +#define ESPI_SYSEVT_INT_T1 0x114 +#define ESPI_SYSEVT_INT_T2 0x118 +#define ESPI_SYSEVT_INT_T2_HOST_RST_WARN ESPI_SYSEVT_HOST_RST_WARN +#define ESPI_SYSEVT_INT_T2_OOB_RST_WARN ESPI_SYSEVT_OOB_RST_WARN +#define ESPI_SYSEVT_INT_STS 0x11c +#define ESPI_SYSEVT_INT_STS_NMI_OUT ESPI_SYSEVT_NMI_OUT +#define ESPI_SYSEVT_INT_STS_SMI_OUT ESPI_SYSEVT_SMI_OUT +#define ESPI_SYSEVT_INT_STS_HOST_RST_WARN ESPI_SYSEVT_HOST_RST_WARN +#define ESPI_SYSEVT_INT_STS_OOB_RST_WARN ESPI_SYSEVT_OOB_RST_WARN +#define ESPI_SYSEVT_INT_STS_PLTRSTN ESPI_SYSEVT_PLTRSTN +#define ESPI_SYSEVT_INT_STS_SUSPEND ESPI_SYSEVT_SUSPEND +#define ESPI_SYSEVT_INT_STS_S5_SLEEP ESPI_SYSEVT_INT_S5_SLEEP +#define ESPI_SYSEVT_INT_STS_S4_SLEEP ESPI_SYSEVT_INT_S4_SLEEP +#define ESPI_SYSEVT_INT_STS_S3_SLEEP ESPI_SYSEVT_INT_S3_SLEEP +#define ESPI_SYSEVT1_INT_T0 0x120 +#define ESPI_SYSEVT1_INT_T1 0x124 +#define ESPI_SYSEVT1_INT_T2 0x128 +#define ESPI_SYSEVT1_INT_STS 0x12c +#define ESPI_SYSEVT1_INT_STS_SUSPEND_WARN ESPI_SYSEVT1_SUSPEND_WARN +#define ESPI_OOB_RX_DMA_RB_SIZE 0x130 +#define ESPI_OOB_RX_DMA_RD_PTR 0x134 +#define ESPI_OOB_RX_DMA_RD_PTR_UPDATE BIT(31) +#define ESPI_OOB_RX_DMA_WS_PTR 0x138 +#define ESPI_OOB_RX_DMA_WS_PTR_RECV_EN BIT(31) +#define ESPI_OOB_RX_DMA_WS_PTR_SP_MASK GENMASK(27, 16) +#define ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT 16 +#define ESPI_OOB_RX_DMA_WS_PTR_WP_MASK GENMASK(11, 0) +#define ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT 0 +#define ESPI_OOB_TX_DMA_RB_SIZE 0x140 +#define ESPI_OOB_TX_DMA_RD_PTR 0x144 +#define ESPI_OOB_TX_DMA_RD_PTR_UPDATE BIT(31) +#define ESPI_OOB_TX_DMA_WR_PTR 0x148 +#define ESPI_OOB_TX_DMA_WR_PTR_SEND_EN BIT(31) + +/* collect ESPI_INT_STS bits of eSPI channels for convenience */ +#define ESPI_INT_STS_PERIF_BITS \ + (ESPI_INT_STS_PERIF_NP_TX_ABT | \ + ESPI_INT_STS_PERIF_PC_TX_ABT | \ + ESPI_INT_STS_PERIF_NP_RX_ABT | \ + ESPI_INT_STS_PERIF_PC_RX_ABT | \ + ESPI_INT_STS_PERIF_NP_TX_ERR | \ + ESPI_INT_STS_PERIF_PC_TX_ERR | \ + ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ + ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ + ESPI_INT_STS_PERIF_PC_RX_CMPLT) + +#define ESPI_INT_STS_VW_BITS \ + (ESPI_INT_STS_VW_SYSEVT1 | \ + ESPI_INT_STS_VW_GPIOEVT | \ + ESPI_INT_STS_VW_SYSEVT) + +#define ESPI_INT_STS_OOB_BITS \ + (ESPI_INT_STS_OOB_RX_TMOUT | \ + ESPI_INT_STS_OOB_TX_ERR | \ + ESPI_INT_STS_OOB_TX_ABT | \ + ESPI_INT_STS_OOB_RX_ABT | \ + ESPI_INT_STS_OOB_TX_CMPLT | \ + ESPI_INT_STS_OOB_RX_CMPLT) + +#define ESPI_INT_STS_FLASH_BITS \ + (ESPI_INT_STS_FLASH_TX_ERR | \ + ESPI_INT_STS_FLASH_TX_ABT | \ + ESPI_INT_STS_FLASH_RX_ABT | \ + ESPI_INT_STS_FLASH_TX_CMPLT | \ + ESPI_INT_STS_FLASH_RX_CMPLT) + +/* collect ESPI_INT_EN bits of eSPI channels for convenience */ +#define ESPI_INT_EN_PERIF_BITS \ + (ESPI_INT_EN_PERIF_NP_TX_ABT | \ + ESPI_INT_EN_PERIF_PC_TX_ABT | \ + ESPI_INT_EN_PERIF_NP_RX_ABT | \ + ESPI_INT_EN_PERIF_PC_RX_ABT | \ + ESPI_INT_EN_PERIF_NP_TX_ERR | \ + ESPI_INT_EN_PERIF_PC_TX_ERR | \ + ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ + ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ + ESPI_INT_EN_PERIF_PC_RX_CMPLT) + +#define ESPI_INT_EN_VW_BITS \ + (ESPI_INT_EN_VW_SYSEVT1 | \ + ESPI_INT_EN_VW_GPIOEVT | \ + ESPI_INT_EN_VW_SYSEVT) + +#define ESPI_INT_EN_OOB_BITS \ + (ESPI_INT_EN_OOB_RX_TMOUT | \ + ESPI_INT_EN_OOB_TX_ERR | \ + ESPI_INT_EN_OOB_TX_ABT | \ + ESPI_INT_EN_OOB_RX_ABT | \ + ESPI_INT_EN_OOB_TX_CMPLT | \ + ESPI_INT_EN_OOB_RX_CMPLT) + +#define ESPI_INT_EN_FLASH_BITS \ + (ESPI_INT_EN_FLASH_TX_ERR | \ + ESPI_INT_EN_FLASH_TX_ABT | \ + ESPI_INT_EN_FLASH_RX_ABT | \ + ESPI_INT_EN_FLASH_TX_CMPLT | \ + ESPI_INT_EN_FLASH_RX_CMPLT) + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-flash.h b/drivers/soc/aspeed/aspeed-espi-flash.h new file mode 100644 index 000000000000..1acb9877cb2c --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-flash.h @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 ASPEED Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI flash channel. It is part of Aspeed eSPI slave + * driver implementation + */ +#ifndef _ASPEED_ESPI_FLASH_H_ +#define _ASPEED_ESPI_FLASH_H_ + +#define FLASH_MDEV_NAME "aspeed-espi-flash" + +enum aspeed_espi_flash_safs_mode { + SAFS_MODE_MIX, + SAFS_MODE_SW, + SAFS_MODE_HW, + SAFS_MODES, +}; + +struct aspeed_espi_flash_dma { + void *tx_virt; + dma_addr_t tx_addr; + void *rx_virt; + dma_addr_t rx_addr; +}; + +struct aspeed_espi_flash { + uint32_t safs_mode; + + uint32_t dma_mode; + struct aspeed_espi_flash_dma dma; + + uint32_t rx_ready; + wait_queue_head_t wq; + + struct mutex get_rx_mtx; + struct mutex put_tx_mtx; + + spinlock_t lock; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +static long aspeed_espi_flash_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_flash *espi_flash) +{ + int i, rc = 0; + unsigned long flags; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl; + + if (fp->f_flags & O_NONBLOCK) { + if (mutex_trylock(&espi_flash->get_rx_mtx)) + return -EBUSY; + + if (!espi_flash->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { + mutex_lock(&espi_flash->get_rx_mtx); + + if (!espi_flash->rx_ready) { + rc = wait_event_interruptible(espi_flash->wq, + espi_flash->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; + } + } + } + + /* common header (i.e. cycle type, tag, and length) is taken by HW */ + regmap_read(espi_ctrl->map, ESPI_FLASH_RX_CTRL, ®); + cyc = (reg & ESPI_FLASH_RX_CTRL_CYC_MASK) >> ESPI_FLASH_RX_CTRL_CYC_SHIFT; + tag = (reg & ESPI_FLASH_RX_CTRL_TAG_MASK) >> ESPI_FLASH_RX_CTRL_TAG_SHIFT; + len = (reg & ESPI_FLASH_RX_CTRL_LEN_MASK) >> ESPI_FLASH_RX_CTRL_LEN_SHIFT; + + /* + * calculate the length of the rest part of the + * eSPI packet to be read from HW and copied to + * user space. + */ + switch (cyc) { + case ESPI_FLASH_READ: + case ESPI_FLASH_WRITE: + case ESPI_FLASH_ERASE: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_flash_rwe); + break; + case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: + case ESPI_FLASH_SUC_CMPLT_D_FIRST: + case ESPI_FLASH_SUC_CMPLT_D_LAST: + case ESPI_FLASH_SUC_CMPLT_D_ONLY: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_flash_cmplt); + break; + case ESPI_FLASH_SUC_CMPLT: + case ESPI_FLASH_UNSUC_CMPLT: + pkt_len = len + sizeof(struct espi_flash_cmplt); + break; + default: + rc = -EFAULT; + goto unlock_mtx_n_out; + } + + if (ioc->pkt_len < pkt_len) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = cyc; + hdr->tag = tag; + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + + if (espi_flash->dma_mode) { + memcpy(hdr + 1, espi_flash->dma.rx_virt, + pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) { + regmap_read(espi_ctrl->map, + ESPI_FLASH_RX_PORT, ®); + pkt[i] = reg & 0xff; + } + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + spin_lock_irqsave(&espi_flash->lock, flags); + + regmap_write_bits(espi_ctrl->map, ESPI_FLASH_RX_CTRL, + ESPI_FLASH_RX_CTRL_PEND_SERV, + ESPI_FLASH_RX_CTRL_PEND_SERV); + + espi_flash->rx_ready = 0; + + spin_unlock_irqrestore(&espi_flash->lock, flags); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_flash->get_rx_mtx); + + return rc; +} + +static long aspeed_espi_flash_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_flash *espi_flash) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl; + + if (!mutex_trylock(&espi_flash->put_tx_mtx)) + return -EAGAIN; + + regmap_read(espi_ctrl->map, ESPI_FLASH_TX_CTRL, ®); + if (reg & ESPI_FLASH_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_flash->dma_mode) { + memcpy(espi_flash->dma.tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_FLASH_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_FLASH_TX_CTRL_CYC_SHIFT) & ESPI_FLASH_TX_CTRL_CYC_MASK) + | ((tag << ESPI_FLASH_TX_CTRL_TAG_SHIFT) & ESPI_FLASH_TX_CTRL_TAG_MASK) + | ((len << ESPI_FLASH_TX_CTRL_LEN_SHIFT) & ESPI_FLASH_TX_CTRL_LEN_MASK) + | ESPI_FLASH_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_FLASH_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_flash->put_tx_mtx); + + return rc; +} + +static long aspeed_espi_flash_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct aspeed_espi_ioc ioc; + struct aspeed_espi_flash *espi_flash = container_of( + fp->private_data, + struct aspeed_espi_flash, + mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; + + if (ioc.pkt_len > ESPI_PKT_LEN_MAX) + return -EINVAL; + + switch (cmd) { + case ASPEED_ESPI_FLASH_GET_RX: + return aspeed_espi_flash_get_rx(fp, &ioc, espi_flash); + case ASPEED_ESPI_FLASH_PUT_TX: + return aspeed_espi_flash_put_tx(fp, &ioc, espi_flash); + }; + + return -EINVAL; +} + +static const struct file_operations aspeed_espi_flash_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aspeed_espi_flash_ioctl, +}; + +static void aspeed_espi_flash_event(uint32_t sts, struct aspeed_espi_flash *espi_flash) +{ + unsigned long flags; + + if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { + spin_lock_irqsave(&espi_flash->lock, flags); + espi_flash->rx_ready = 1; + spin_unlock_irqrestore(&espi_flash->lock, flags); + wake_up_interruptible(&espi_flash->wq); + } +} + +static void aspeed_espi_flash_enable(struct aspeed_espi_flash *espi_flash) +{ + struct aspeed_espi_flash_dma *dma = &espi_flash->dma; + struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl; + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_FLASH_SW_MODE_MASK, + (espi_flash->safs_mode << ESPI_CTRL_FLASH_SW_MODE_SHIFT)); + + if (espi_flash->dma_mode) { + regmap_write(espi_ctrl->map, ESPI_FLASH_TX_DMA, dma->tx_addr); + regmap_write(espi_ctrl->map, ESPI_FLASH_RX_DMA, dma->rx_addr); + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_FLASH_TX_DMA_EN | ESPI_CTRL_FLASH_RX_DMA_EN, + ESPI_CTRL_FLASH_TX_DMA_EN | ESPI_CTRL_FLASH_RX_DMA_EN); + } + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_FLASH_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_FLASH_BITS, + ESPI_INT_EN_FLASH_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_FLASH_SW_RDY, + ESPI_CTRL_FLASH_SW_RDY); +} + +static void *aspeed_espi_flash_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc = 0; + struct aspeed_espi_flash *espi_flash; + struct aspeed_espi_flash_dma *dma; + + espi_flash = devm_kzalloc(dev, sizeof(*espi_flash), GFP_KERNEL); + if (!espi_flash) + return ERR_PTR(-ENOMEM); + + espi_flash->ctrl = espi_ctrl; + + init_waitqueue_head(&espi_flash->wq); + + spin_lock_init(&espi_flash->lock); + + mutex_init(&espi_flash->put_tx_mtx); + mutex_init(&espi_flash->get_rx_mtx); + + if (of_property_read_bool(dev->of_node, "flash,dma-mode")) + espi_flash->dma_mode = 1; + + of_property_read_u32(dev->of_node, "flash,safs-mode", &espi_flash->safs_mode); + if (espi_flash->safs_mode >= SAFS_MODES) { + dev_err(dev, "invalid SAFS mode\n"); + return ERR_PTR(-EINVAL); + } + + if (espi_flash->dma_mode) { + dma = &espi_flash->dma; + + dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->tx_addr, GFP_KERNEL); + if (!dma->tx_virt) { + dev_err(dev, "cannot allocate DMA TX buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->rx_addr, GFP_KERNEL); + if (!dma->rx_virt) { + dev_err(dev, "cannot allocate DMA RX buffer\n"); + return ERR_PTR(-ENOMEM); + } + } + + espi_flash->mdev.parent = dev; + espi_flash->mdev.minor = MISC_DYNAMIC_MINOR; + espi_flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", FLASH_MDEV_NAME); + espi_flash->mdev.fops = &aspeed_espi_flash_fops; + rc = misc_register(&espi_flash->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_flash_enable(espi_flash); + + return espi_flash; +} + +static void aspeed_espi_flash_free(struct device *dev, struct aspeed_espi_flash *espi_flash) +{ + struct aspeed_espi_flash_dma *dma = &espi_flash->dma; + + if (espi_flash->dma_mode) { + dma_free_coherent(dev, PAGE_SIZE, dma->tx_virt, dma->tx_addr); + dma_free_coherent(dev, PAGE_SIZE, dma->rx_virt, dma->rx_addr); + } + + mutex_destroy(&espi_flash->put_tx_mtx); + mutex_destroy(&espi_flash->get_rx_mtx); + + misc_deregister(&espi_flash->mdev); +} + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-ioc.h b/drivers/soc/aspeed/aspeed-espi-ioc.h new file mode 100644 index 000000000000..e93739c80b5f --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-ioc.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#ifndef _ASPEED_ESPI_IOC_H +#define _ASPEED_ESPI_IOC_H + +#include +#include + +/* + * eSPI cycle type encoding + * + * Section 5.1 Cycle Types and Packet Format, + * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. + */ +#define ESPI_PERIF_MEMRD32 0x00 +#define ESPI_PERIF_MEMRD64 0x02 +#define ESPI_PERIF_MEMWR32 0x01 +#define ESPI_PERIF_MEMWR64 0x03 +#define ESPI_PERIF_MSG 0x10 +#define ESPI_PERIF_MSG_D 0x11 +#define ESPI_PERIF_SUC_CMPLT 0x06 +#define ESPI_PERIF_SUC_CMPLT_D_MIDDLE 0x09 +#define ESPI_PERIF_SUC_CMPLT_D_FIRST 0x0b +#define ESPI_PERIF_SUC_CMPLT_D_LAST 0x0d +#define ESPI_PERIF_SUC_CMPLT_D_ONLY 0x0f +#define ESPI_PERIF_UNSUC_CMPLT 0x0c +#define ESPI_OOB_MSG 0x21 +#define ESPI_FLASH_READ 0x00 +#define ESPI_FLASH_WRITE 0x01 +#define ESPI_FLASH_ERASE 0x02 +#define ESPI_FLASH_SUC_CMPLT 0x06 +#define ESPI_FLASH_SUC_CMPLT_D_MIDDLE 0x09 +#define ESPI_FLASH_SUC_CMPLT_D_FIRST 0x0b +#define ESPI_FLASH_SUC_CMPLT_D_LAST 0x0d +#define ESPI_FLASH_SUC_CMPLT_D_ONLY 0x0f +#define ESPI_FLASH_UNSUC_CMPLT 0x0c + +/* + * eSPI packet format structure + * + * Section 5.1 Cycle Types and Packet Format, + * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. + */ +struct espi_comm_hdr { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; +}; + +struct espi_perif_mem32 { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint32_t addr_be; + uint8_t data[]; +} __packed; + +struct espi_perif_mem64 { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint32_t addr_be; + uint8_t data[]; +} __packed; + +struct espi_perif_msg { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t msg_code; + uint8_t msg_byte[4]; + uint8_t data[]; +} __packed; + +struct espi_perif_cmplt { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t data[]; +} __packed; + +struct espi_oob_msg { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t data[]; +}; + +struct espi_flash_rwe { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint32_t addr_be; + uint8_t data[]; +} __packed; + +struct espi_flash_cmplt { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t data[]; +} __packed; + +struct aspeed_espi_ioc { + uint32_t pkt_len; + uint8_t *pkt; +}; + +/* + * we choose the longest header and the max payload size + * based on the Intel specification to define the maximum + * eSPI packet length + */ +#define ESPI_PLD_LEN_MIN (1UL << 6) +#define ESPI_PLD_LEN_MAX (1UL << 12) +#define ESPI_PKT_LEN_MAX (sizeof(struct espi_perif_msg) + ESPI_PLD_LEN_MAX) + +#define __ASPEED_ESPI_IOCTL_MAGIC 0xb8 + +/* peripheral channel (ch0) */ +#define ASPEED_ESPI_PERIF_PC_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x00, struct aspeed_espi_ioc) +#define ASPEED_ESPI_PERIF_PC_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x01, struct aspeed_espi_ioc) +#define ASPEED_ESPI_PERIF_NP_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x02, struct aspeed_espi_ioc) +/* peripheral channel (ch1) */ +#define ASPEED_ESPI_VW_GET_GPIO_VAL _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x10, uint8_t) +#define ASPEED_ESPI_VW_PUT_GPIO_VAL _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x11, uint8_t) +/* out-of-band channel (ch2) */ +#define ASPEED_ESPI_OOB_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x20, struct aspeed_espi_ioc) +#define ASPEED_ESPI_OOB_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x21, struct aspeed_espi_ioc) +/* flash channel (ch3) */ +#define ASPEED_ESPI_FLASH_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x30, struct aspeed_espi_ioc) +#define ASPEED_ESPI_FLASH_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x31, struct aspeed_espi_ioc) + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-oob.h b/drivers/soc/aspeed/aspeed-espi-oob.h new file mode 100644 index 000000000000..182a9ca1e4cc --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-oob.h @@ -0,0 +1,611 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Aspeed Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI out-of-band channel. It is part of Aspeed eSPI + * slave driver implementation + */ +#ifndef _ASPEED_ESPI_OOB_H_ +#define _ASPEED_ESPI_OOB_H_ + +#define OOB_MDEV_NAME "aspeed-espi-oob" + +/* DMA descriptor is supported since AST2600 */ +#define OOB_DMA_DESC_MAX_NUM 1024 + +/* DMA TX descriptor type */ +#define OOB_DMA_TX_DESC_CUST 0x04 + +struct oob_tx_dma_desc { + uint32_t data_addr; + uint8_t cyc; + uint16_t tag : 4; + uint16_t len : 12; + uint8_t msg_type : 3; + uint8_t raz0 : 1; + uint8_t pec : 1; + uint8_t int_en : 1; + uint8_t pause : 1; + uint8_t raz1 : 1; + uint32_t raz2; + uint32_t raz3; +} __packed; + +struct oob_rx_dma_desc { + uint32_t data_addr; + uint8_t cyc; + uint16_t tag : 4; + uint16_t len : 12; + uint8_t raz : 7; + uint8_t dirty : 1; +} __packed; + +struct aspeed_espi_oob_dma { + uint32_t tx_desc_num; + uint32_t rx_desc_num; + + struct oob_tx_dma_desc *tx_desc; + dma_addr_t tx_desc_addr; + + struct oob_rx_dma_desc *rx_desc; + dma_addr_t rx_desc_addr; + + void *tx_virt; + dma_addr_t tx_addr; + + void *rx_virt; + dma_addr_t rx_addr; +}; + +struct aspeed_espi_oob { + uint32_t dma_mode; + struct aspeed_espi_oob_dma dma; + + uint32_t rx_ready; + wait_queue_head_t wq; + + struct mutex get_rx_mtx; + struct mutex put_tx_mtx; + + spinlock_t lock; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +/* descriptor-based RX DMA handling */ +static long aspeed_espi_oob_dma_desc_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int rc = 0; + unsigned long flags; + uint32_t reg; + uint32_t wptr, sptr; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + struct oob_rx_dma_desc *d; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + regmap_read(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, ®); + wptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_WP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT; + sptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_SP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT; + + d = &espi_oob->dma.rx_desc[sptr]; + + if (!d->dirty) + return -EFAULT; + + pkt_len = ((d->len) ? d->len : 0x1000) + sizeof(struct espi_comm_hdr); + + if (ioc->pkt_len < pkt_len) + return -EINVAL; + + pkt = vmalloc(pkt_len); + if (!pkt) + return -ENOMEM; + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = d->cyc; + hdr->tag = d->tag; + hdr->len_h = d->len >> 8; + hdr->len_l = d->len & 0xff; + memcpy(hdr + 1, espi_oob->dma.rx_virt + (PAGE_SIZE * sptr), pkt_len - sizeof(*hdr)); + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + spin_lock_irqsave(&espi_oob->lock, flags); + + /* make current descriptor available again */ + d->dirty = 0; + + sptr = (sptr + 1) % espi_oob->dma.rx_desc_num; + wptr = (wptr + 1) % espi_oob->dma.rx_desc_num; + + reg = ((wptr << ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT) & ESPI_OOB_RX_DMA_WS_PTR_WP_MASK) + | ((sptr << ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT) & ESPI_OOB_RX_DMA_WS_PTR_SP_MASK) + | ESPI_OOB_RX_DMA_WS_PTR_RECV_EN; + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, reg); + + /* set ready flag base on the next RX descriptor */ + espi_oob->rx_ready = espi_oob->dma.rx_desc[sptr].dirty; + + spin_unlock_irqrestore(&espi_oob->lock, flags); + +free_n_out: + vfree(pkt); + + return rc; +} + +static long aspeed_espi_oob_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int i, rc = 0; + unsigned long flags; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + if (fp->f_flags & O_NONBLOCK) { + if (mutex_trylock(&espi_oob->get_rx_mtx)) + return -EBUSY; + + if (!espi_oob->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { + mutex_lock(&espi_oob->get_rx_mtx); + + if (!espi_oob->rx_ready) { + rc = wait_event_interruptible(espi_oob->wq, + espi_oob->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; + } + } + } + + if (espi_oob->dma_mode && espi_ctrl->version != ESPI_AST2500) { + rc = aspeed_espi_oob_dma_desc_get_rx(fp, ioc, espi_oob); + goto unlock_mtx_n_out; + } + + /* common header (i.e. cycle type, tag, and length) is taken by HW */ + regmap_read(espi_ctrl->map, ESPI_OOB_RX_CTRL, ®); + cyc = (reg & ESPI_OOB_RX_CTRL_CYC_MASK) >> ESPI_OOB_RX_CTRL_CYC_SHIFT; + tag = (reg & ESPI_OOB_RX_CTRL_TAG_MASK) >> ESPI_OOB_RX_CTRL_TAG_SHIFT; + len = (reg & ESPI_OOB_RX_CTRL_LEN_MASK) >> ESPI_OOB_RX_CTRL_LEN_SHIFT; + + /* + * calculate the length of the rest part of the + * eSPI packet to be read from HW and copied to + * user space. + */ + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + sizeof(struct espi_comm_hdr); + + if (ioc->pkt_len < pkt_len) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = cyc; + hdr->tag = tag; + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + + if (espi_oob->dma_mode) { + memcpy(hdr + 1, espi_oob->dma.rx_virt, + pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) { + regmap_read(espi_ctrl->map, + ESPI_OOB_RX_PORT, ®); + pkt[i] = reg & 0xff; + } + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + spin_lock_irqsave(&espi_oob->lock, flags); + + regmap_write_bits(espi_ctrl->map, ESPI_OOB_RX_CTRL, + ESPI_OOB_RX_CTRL_PEND_SERV, + ESPI_OOB_RX_CTRL_PEND_SERV); + + espi_oob->rx_ready = 0; + + spin_unlock_irqrestore(&espi_oob->lock, flags); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_oob->get_rx_mtx); + + return rc; +} + +/* descriptor-based TX DMA handling */ +static long aspeed_espi_oob_dma_desc_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int rc = 0; + uint32_t rptr, wptr; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct oob_tx_dma_desc *d; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + pkt = vzalloc(ioc->pkt_len); + if (!pkt) + return -ENOMEM; + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* kick HW to reflect the up-to-date read/write pointer */ + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_RD_PTR, + ESPI_OOB_TX_DMA_RD_PTR_UPDATE); + + regmap_read(espi_ctrl->map, ESPI_OOB_TX_DMA_RD_PTR, &rptr); + regmap_read(espi_ctrl->map, ESPI_OOB_TX_DMA_WR_PTR, &wptr); + + if (((wptr + 1) % espi_oob->dma.tx_desc_num) == rptr) + return -EBUSY; + + d = &espi_oob->dma.tx_desc[wptr]; + d->cyc = hdr->cyc; + d->tag = hdr->tag; + d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + d->msg_type = OOB_DMA_TX_DESC_CUST; + + memcpy(espi_oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + + dma_wmb(); + + wptr = (wptr + 1) % espi_oob->dma.tx_desc_num; + wptr |= ESPI_OOB_TX_DMA_WR_PTR_SEND_EN; + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_WR_PTR, wptr); + +free_n_out: + vfree(pkt); + + return rc; +} + +static long aspeed_espi_oob_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + if (!mutex_trylock(&espi_oob->put_tx_mtx)) + return -EBUSY; + + if (espi_oob->dma_mode && espi_ctrl->version != ESPI_AST2500) { + rc = aspeed_espi_oob_dma_desc_put_tx(fp, ioc, espi_oob); + goto unlock_mtx_n_out; + } + + regmap_read(espi_ctrl->map, ESPI_OOB_TX_CTRL, ®); + if (reg & ESPI_OOB_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } + + if (ioc->pkt_len > ESPI_PKT_LEN_MAX) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_oob->dma_mode) { + memcpy(espi_oob->dma.tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_OOB_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_OOB_TX_CTRL_CYC_SHIFT) & ESPI_OOB_TX_CTRL_CYC_MASK) + | ((tag << ESPI_OOB_TX_CTRL_TAG_SHIFT) & ESPI_OOB_TX_CTRL_TAG_MASK) + | ((len << ESPI_OOB_TX_CTRL_LEN_SHIFT) & ESPI_OOB_TX_CTRL_LEN_MASK) + | ESPI_OOB_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_OOB_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_oob->put_tx_mtx); + + return rc; +} + +static long aspeed_espi_oob_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct aspeed_espi_ioc ioc; + struct aspeed_espi_oob *espi_oob = container_of( + fp->private_data, + struct aspeed_espi_oob, + mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; + + if (ioc.pkt_len > ESPI_PKT_LEN_MAX) + return -EINVAL; + + switch (cmd) { + case ASPEED_ESPI_OOB_GET_RX: + return aspeed_espi_oob_get_rx(fp, &ioc, espi_oob); + case ASPEED_ESPI_OOB_PUT_TX: + return aspeed_espi_oob_put_tx(fp, &ioc, espi_oob); + }; + + return -EINVAL; +} + +static const struct file_operations aspeed_espi_oob_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aspeed_espi_oob_ioctl, +}; + +static void aspeed_espi_oob_event(uint32_t sts, struct aspeed_espi_oob *espi_oob) +{ + unsigned long flags; + + if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { + spin_lock_irqsave(&espi_oob->lock, flags); + espi_oob->rx_ready = 1; + spin_unlock_irqrestore(&espi_oob->lock, flags); + + wake_up_interruptible(&espi_oob->wq); + } +} + +static void aspeed_espi_oob_enable(struct aspeed_espi_oob *espi_oob) +{ + int i; + struct aspeed_espi_oob_dma *dma = &espi_oob->dma; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_SW_RDY | ESPI_CTRL_OOB_RX_SW_RST, 0); + + if (espi_oob->dma_mode) + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN, 0); + else + regmap_write(espi_ctrl->map, ESPI_OOB_RX_CTRL, ESPI_OOB_RX_CTRL_PEND_SERV); + + /* + * cleanup OOB RX FIFO to get rid of the data + * of OOB early init side-effect + */ + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_RX_SW_RST, ESPI_CTRL_OOB_RX_SW_RST); + + regmap_write(espi_ctrl->map, ESPI_OOB_RX_CTRL, + ESPI_OOB_RX_CTRL_PEND_SERV); + + if (espi_oob->dma_mode) { + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN, + ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN); + + if (espi_ctrl->version == ESPI_AST2500) { + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA, dma->tx_addr); + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA, dma->rx_addr); + } else { + for (i = 0; i < dma->tx_desc_num; ++i) + dma->tx_desc[i].data_addr = dma->tx_addr + (i * PAGE_SIZE); + + for (i = 0; i < dma->rx_desc_num; ++i) { + dma->rx_desc[i].data_addr = dma->rx_addr + (i * PAGE_SIZE); + dma->rx_desc[i].dirty = 0; + } + + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA, dma->tx_desc_addr); + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_RB_SIZE, dma->tx_desc_num); + + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA, dma->rx_desc_addr); + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA_RB_SIZE, dma->rx_desc_num); + regmap_update_bits(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, + ESPI_OOB_RX_DMA_WS_PTR_RECV_EN, + ESPI_OOB_RX_DMA_WS_PTR_RECV_EN); + } + } + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_OOB_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_OOB_BITS, + ESPI_INT_EN_OOB_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_SW_RDY, + ESPI_CTRL_OOB_SW_RDY); +} + +static void *aspeed_espi_oob_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc = 0; + struct aspeed_espi_oob *espi_oob; + struct aspeed_espi_oob_dma *dma; + + espi_oob = devm_kzalloc(dev, sizeof(*espi_oob), GFP_KERNEL); + if (!espi_oob) + return ERR_PTR(-ENOMEM); + + espi_oob->ctrl = espi_ctrl; + + init_waitqueue_head(&espi_oob->wq); + + spin_lock_init(&espi_oob->lock); + + mutex_init(&espi_oob->put_tx_mtx); + mutex_init(&espi_oob->get_rx_mtx); + + if (of_property_read_bool(dev->of_node, "oob,dma-mode")) + espi_oob->dma_mode = 1; + + if (espi_oob->dma_mode) { + dma = &espi_oob->dma; + + /* Descriptor based OOB DMA is supported since AST2600 */ + if (espi_ctrl->version != ESPI_AST2500) { + of_property_read_u32(dev->of_node, "oob,dma-tx-desc-num", + &dma->tx_desc_num); + of_property_read_u32(dev->of_node, "oob,dma-rx-desc-num", + &dma->rx_desc_num); + + if (!dma->tx_desc_num || !dma->rx_desc_num) { + dev_err(dev, "invalid zero number of DMA channels\n"); + return ERR_PTR(-EINVAL); + } + + if (dma->tx_desc_num >= OOB_DMA_DESC_MAX_NUM || + dma->rx_desc_num >= OOB_DMA_DESC_MAX_NUM) { + dev_err(dev, "too many number of DMA channels\n"); + return ERR_PTR(-EINVAL); + } + + dma->tx_desc = dma_alloc_coherent(dev, + sizeof(*dma->tx_desc) * dma->tx_desc_num, + &dma->tx_desc_addr, GFP_KERNEL); + if (!dma->tx_desc) { + dev_err(dev, "cannot allocate DMA TX descriptor\n"); + return ERR_PTR(-ENOMEM); + } + + dma->rx_desc = dma_alloc_coherent(dev, + sizeof(*dma->rx_desc) * dma->rx_desc_num, + &dma->rx_desc_addr, GFP_KERNEL); + if (!dma->rx_desc) { + dev_err(dev, "cannot allocate DMA RX descriptor\n"); + return ERR_PTR(-ENOMEM); + } + } + + /* + * DMA descriptors are consumed in the circular + * queue paradigm. Therefore, one dummy slot is + * reserved to detect the full condition. + * + * For AST2500 without DMA descriptors supported, + * the number of the queue slot should be 1 here. + */ + dma->tx_desc_num += 1; + dma->rx_desc_num += 1; + + dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->tx_desc_num, + &dma->tx_addr, GFP_KERNEL); + if (!dma->tx_virt) { + dev_err(dev, "cannot allocate DMA TX buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->rx_desc_num, + &dma->rx_addr, GFP_KERNEL); + if (!dma->rx_virt) { + dev_err(dev, "cannot allocate DMA RX buffer\n"); + return ERR_PTR(-ENOMEM); + } + } + + espi_oob->mdev.parent = dev; + espi_oob->mdev.minor = MISC_DYNAMIC_MINOR; + espi_oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", OOB_MDEV_NAME); + espi_oob->mdev.fops = &aspeed_espi_oob_fops; + rc = misc_register(&espi_oob->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_oob_enable(espi_oob); + + return espi_oob; +} + +static void aspeed_espi_oob_free(struct device *dev, struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_oob_dma *dma = &espi_oob->dma; + + if (espi_oob->dma_mode) { + dma_free_coherent(dev, sizeof(*dma->tx_desc) * dma->tx_desc_num, + dma->tx_desc, dma->tx_desc_addr); + dma_free_coherent(dev, sizeof(*dma->rx_desc) * dma->rx_desc_num, + dma->rx_desc, dma->rx_desc_addr); + dma_free_coherent(dev, PAGE_SIZE * dma->tx_desc_num, + dma->tx_virt, dma->tx_addr); + dma_free_coherent(dev, PAGE_SIZE * dma->rx_desc_num, + dma->rx_virt, dma->rx_addr); + } + + mutex_destroy(&espi_oob->put_tx_mtx); + mutex_destroy(&espi_oob->get_rx_mtx); + + misc_deregister(&espi_oob->mdev); +} + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-perif.h b/drivers/soc/aspeed/aspeed-espi-perif.h new file mode 100644 index 000000000000..f24818227c18 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-perif.h @@ -0,0 +1,512 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 ASPEED Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI peripheral channel. It is part of Aspeed eSPI + * slave driver implementation + */ +#ifndef _ASPEED_ESPI_PERIF_H_ +#define _ASPEED_ESPI_PERIF_H_ + +#define PERIF_MDEV_NAME "aspeed-espi-peripheral" +#define PERIF_MEMCYC_UNLOCK_KEY 0xfedc756e +#define PERIF_MEMCYC_SIZE_MIN 0x10000 + +struct aspeed_espi_perif_dma { + void *pc_tx_virt; + dma_addr_t pc_tx_addr; + void *pc_rx_virt; + dma_addr_t pc_rx_addr; + void *np_tx_virt; + dma_addr_t np_tx_addr; +}; + +struct aspeed_espi_perif { + uint32_t mcyc_enable; + void *mcyc_virt; + phys_addr_t mcyc_saddr; + phys_addr_t mcyc_taddr; + uint32_t mcyc_size; + uint32_t mcyc_mask; + + uint32_t dma_mode; + struct aspeed_espi_perif_dma dma; + + uint32_t rx_ready; + wait_queue_head_t wq; + + spinlock_t rx_lock; + struct mutex pc_tx_lock; + struct mutex np_tx_lock; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +static long aspeed_espi_perif_pc_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_perif *espi_perif) +{ + int i, rc; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + unsigned long flags; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (!espi_perif->rx_ready) { + if (fp->f_flags & O_NONBLOCK) + return -ENODATA; + + rc = wait_event_interruptible(espi_perif->wq, espi_perif->rx_ready); + if (rc == -ERESTARTSYS) + return -EINTR; + } + + /* common header (i.e. cycle type, tag, and length) is taken by HW */ + regmap_read(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL, ®); + cyc = (reg & ESPI_PERIF_PC_RX_CTRL_CYC_MASK) >> ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT; + tag = (reg & ESPI_PERIF_PC_RX_CTRL_TAG_MASK) >> ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT; + len = (reg & ESPI_PERIF_PC_RX_CTRL_LEN_MASK) >> ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT; + + /* + * calculate the length of the rest part of the + * eSPI packet to be read from HW and copied to + * user space. + */ + switch (cyc) { + case ESPI_PERIF_MSG: + pkt_len = len + sizeof(struct espi_perif_msg); + break; + case ESPI_PERIF_MSG_D: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_perif_msg); + break; + case ESPI_PERIF_SUC_CMPLT_D_MIDDLE: + case ESPI_PERIF_SUC_CMPLT_D_FIRST: + case ESPI_PERIF_SUC_CMPLT_D_LAST: + case ESPI_PERIF_SUC_CMPLT_D_ONLY: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_perif_cmplt); + break; + case ESPI_PERIF_SUC_CMPLT: + case ESPI_PERIF_UNSUC_CMPLT: + pkt_len = len + sizeof(struct espi_perif_cmplt); + break; + default: + return -EFAULT; + } + + if (ioc->pkt_len < pkt_len) + return -EINVAL; + + pkt = vmalloc(pkt_len); + if (!pkt) + return -ENOMEM; + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = cyc; + hdr->tag = tag; + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + + if (espi_perif->dma_mode) { + memcpy(hdr + 1, espi_perif->dma.pc_rx_virt, + pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) { + regmap_read(espi_ctrl->map, + ESPI_PERIF_PC_RX_PORT, ®); + pkt[i] = reg & 0xff; + } + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) + return -EFAULT; + + spin_lock_irqsave(&espi_perif->rx_lock, flags); + + regmap_write_bits(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL, + ESPI_PERIF_PC_RX_CTRL_PEND_SERV, + ESPI_PERIF_PC_RX_CTRL_PEND_SERV); + + espi_perif->rx_ready = 0; + + spin_unlock_irqrestore(&espi_perif->rx_lock, flags); + + return pkt_len; +} + +static long aspeed_espi_perif_pc_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_perif *espi_perif) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (!mutex_trylock(&espi_perif->pc_tx_lock)) + return -EAGAIN; + + regmap_read(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, ®); + if (reg & ESPI_PERIF_PC_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_perif->dma_mode) { + memcpy(espi_perif->dma.pc_tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_PERIF_PC_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_PERIF_PC_TX_CTRL_CYC_SHIFT) & ESPI_PERIF_PC_TX_CTRL_CYC_MASK) + | ((tag << ESPI_PERIF_PC_TX_CTRL_TAG_SHIFT) & ESPI_PERIF_PC_TX_CTRL_TAG_MASK) + | ((len << ESPI_PERIF_PC_TX_CTRL_LEN_SHIFT) & ESPI_PERIF_PC_TX_CTRL_LEN_MASK) + | ESPI_PERIF_PC_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_n_out: + mutex_unlock(&espi_perif->pc_tx_lock); + + return rc; +} + +static long aspeed_espi_perif_np_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_perif *espi_perif) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (!mutex_trylock(&espi_perif->np_tx_lock)) + return -EAGAIN; + + regmap_read(espi_ctrl->map, ESPI_PERIF_NP_TX_CTRL, ®); + if (reg & ESPI_PERIF_NP_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_perif->dma_mode) { + memcpy(espi_perif->dma.np_tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_PERIF_NP_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_PERIF_NP_TX_CTRL_CYC_SHIFT) & ESPI_PERIF_NP_TX_CTRL_CYC_MASK) + | ((tag << ESPI_PERIF_NP_TX_CTRL_TAG_SHIFT) & ESPI_PERIF_NP_TX_CTRL_TAG_MASK) + | ((len << ESPI_PERIF_NP_TX_CTRL_LEN_SHIFT) & ESPI_PERIF_NP_TX_CTRL_LEN_MASK) + | ESPI_PERIF_NP_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_PERIF_NP_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_n_out: + mutex_unlock(&espi_perif->np_tx_lock); + + return rc; + +} + +static long aspeed_espi_perif_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct aspeed_espi_ioc ioc; + struct aspeed_espi_perif *espi_perif = container_of( + fp->private_data, + struct aspeed_espi_perif, + mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; + + if (ioc.pkt_len > ESPI_PKT_LEN_MAX) + return -EINVAL; + + switch (cmd) { + case ASPEED_ESPI_PERIF_PC_GET_RX: + return aspeed_espi_perif_pc_get_rx(fp, &ioc, espi_perif); + case ASPEED_ESPI_PERIF_PC_PUT_TX: + return aspeed_espi_perif_pc_put_tx(fp, &ioc, espi_perif); + case ASPEED_ESPI_PERIF_NP_PUT_TX: + return aspeed_espi_perif_np_put_tx(fp, &ioc, espi_perif); + }; + + return -EINVAL; +} + +static int aspeed_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct aspeed_espi_perif *espi_perif = container_of( + fp->private_data, + struct aspeed_espi_perif, + mdev); + unsigned long vm_size = vma->vm_end - vma->vm_start; + pgprot_t prot = vma->vm_page_prot; + + if (!espi_perif->mcyc_enable) + return -EPERM; + + if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > espi_perif->mcyc_size) + return -EINVAL; + + prot = pgprot_noncached(prot); + + if (remap_pfn_range(vma, vma->vm_start, + (espi_perif->mcyc_taddr >> PAGE_SHIFT) + vma->vm_pgoff, + vm_size, prot)) + return -EAGAIN; + + return 0; +} + +static const struct file_operations aspeed_espi_perif_fops = { + .owner = THIS_MODULE, + .mmap = aspeed_espi_perif_mmap, + .unlocked_ioctl = aspeed_espi_perif_ioctl, +}; + +static void aspeed_espi_perif_event(uint32_t sts, struct aspeed_espi_perif *espi_perif) +{ + unsigned long flags; + + if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { + spin_lock_irqsave(&espi_perif->rx_lock, flags); + espi_perif->rx_ready = 1; + spin_unlock_irqrestore(&espi_perif->rx_lock, flags); + + wake_up_interruptible(&espi_perif->wq); + } +} + +static void aspeed_espi_perif_enable(struct aspeed_espi_perif *espi_perif) +{ + struct aspeed_espi_perif_dma *dma = &espi_perif->dma; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (espi_perif->mcyc_enable) { + if (espi_ctrl->version == ESPI_AST2500) { + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK, + PERIF_MEMCYC_UNLOCK_KEY); + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK, + espi_perif->mcyc_mask); + } else { + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK, + espi_perif->mcyc_mask | ESPI_PERIF_PC_RX_MASK_CFG_WP); + regmap_update_bits(espi_ctrl->map, ESPI_CTRL2, + ESPI_CTRL2_MEMCYC_RD_DIS | ESPI_CTRL2_MEMCYC_WR_DIS, 0); + } + + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_SADDR, espi_perif->mcyc_saddr); + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_TADDR, espi_perif->mcyc_taddr); + } + + if (espi_perif->dma_mode) { + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_DMA, dma->pc_rx_addr); + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_TX_DMA, dma->pc_tx_addr); + regmap_write(espi_ctrl->map, ESPI_PERIF_NP_TX_DMA, dma->np_tx_addr); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_PERIF_NP_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_RX_DMA_EN, + ESPI_CTRL_PERIF_NP_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_RX_DMA_EN); + } + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_PERIF_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_PERIF_BITS, + ESPI_INT_EN_PERIF_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_PERIF_SW_RDY, + ESPI_CTRL_PERIF_SW_RDY); +} + +static void *aspeed_espi_perif_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc; + struct aspeed_espi_perif *espi_perif; + struct aspeed_espi_perif_dma *dma; + + espi_perif = devm_kzalloc(dev, sizeof(*espi_perif), GFP_KERNEL); + if (!espi_perif) + return ERR_PTR(-ENOMEM); + + espi_perif->ctrl = espi_ctrl; + + init_waitqueue_head(&espi_perif->wq); + + spin_lock_init(&espi_perif->rx_lock); + + mutex_init(&espi_perif->pc_tx_lock); + mutex_init(&espi_perif->np_tx_lock); + + espi_perif->mcyc_enable = of_property_read_bool(dev->of_node, "perif,memcyc-enable"); + if (espi_perif->mcyc_enable) { + rc = of_property_read_u32(dev->of_node, "perif,memcyc-src-addr", + &espi_perif->mcyc_saddr); + if (rc) { + dev_err(dev, "cannot get Host source address for memory cycle\n"); + return ERR_PTR(-ENODEV); + } + + rc = of_property_read_u32(dev->of_node, "perif,memcyc-size", + &espi_perif->mcyc_size); + if (rc) { + dev_err(dev, "cannot get size for memory cycle\n"); + return ERR_PTR(-ENODEV); + } + + if (espi_perif->mcyc_size < PERIF_MEMCYC_SIZE_MIN) + espi_perif->mcyc_size = PERIF_MEMCYC_SIZE_MIN; + else + espi_perif->mcyc_size = roundup_pow_of_two(espi_perif->mcyc_size); + + espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1); + espi_perif->mcyc_virt = dma_alloc_coherent(dev, espi_perif->mcyc_size, + &espi_perif->mcyc_taddr, GFP_KERNEL); + if (!espi_perif->mcyc_virt) { + dev_err(dev, "cannot allocate memory cycle region\n"); + return ERR_PTR(-ENOMEM); + } + } + + if (of_property_read_bool(dev->of_node, "perif,dma-mode")) { + dma = &espi_perif->dma; + + dma->pc_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->pc_tx_addr, GFP_KERNEL); + if (!dma->pc_tx_virt) { + dev_err(dev, "cannot allocate posted TX DMA buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->pc_rx_addr, GFP_KERNEL); + if (!dma->pc_rx_virt) { + dev_err(dev, "cannot allocate posted RX DMA buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->np_tx_addr, GFP_KERNEL); + if (!dma->np_tx_virt) { + dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); + return ERR_PTR(-ENOMEM); + } + + espi_perif->dma_mode = 1; + } + + espi_perif->mdev.parent = dev; + espi_perif->mdev.minor = MISC_DYNAMIC_MINOR; + espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", PERIF_MDEV_NAME); + espi_perif->mdev.fops = &aspeed_espi_perif_fops; + rc = misc_register(&espi_perif->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_perif_enable(espi_perif); + + return espi_perif; +} + +static void aspeed_espi_perif_free(struct device *dev, struct aspeed_espi_perif *espi_perif) +{ + struct aspeed_espi_perif_dma *dma = &espi_perif->dma; + + if (espi_perif->mcyc_virt) + dma_free_coherent(dev, espi_perif->mcyc_size, + espi_perif->mcyc_virt, + espi_perif->mcyc_taddr); + + if (espi_perif->dma_mode) { + dma_free_coherent(dev, PAGE_SIZE, dma->pc_tx_virt, + dma->pc_tx_addr); + dma_free_coherent(dev, PAGE_SIZE, dma->pc_rx_virt, + dma->pc_rx_addr); + dma_free_coherent(dev, PAGE_SIZE, dma->np_tx_virt, + dma->np_tx_addr); + } + + mutex_destroy(&espi_perif->pc_tx_lock); + mutex_destroy(&espi_perif->np_tx_lock); + + misc_deregister(&espi_perif->mdev); +} + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-vw.h b/drivers/soc/aspeed/aspeed-espi-vw.h new file mode 100644 index 000000000000..3d49366c912e --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-vw.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 ASPEED Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI virtual wire channel. It is part of Aspeed eSPI + * slave driver implementation + */ +#ifndef _ASPEED_ESPI_VW_H_ +#define _ASPEED_ESPI_VW_H_ + +#define VW_MDEV_NAME "aspeed-espi-vw" + +struct aspeed_espi_vw { + int irq; + int irq_reset; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +static long aspeed_espi_vw_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + uint32_t val; + + struct aspeed_espi_vw *espi_vw = container_of( + fp->private_data, + struct aspeed_espi_vw, + mdev); + struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl; + + switch (cmd) { + case ASPEED_ESPI_VW_GET_GPIO_VAL: + regmap_read(espi_ctrl->map, ESPI_VW_GPIO_VAL, &val); + if (put_user(val, (uint32_t __user *)arg)) + return -EFAULT; + break; + + case ASPEED_ESPI_VW_PUT_GPIO_VAL: + if (get_user(val, (uint32_t __user *)arg)) + return -EFAULT; + regmap_write(espi_ctrl->map, ESPI_VW_GPIO_VAL, val); + break; + + default: + return -EINVAL; + }; + + return 0; +} + +static const struct file_operations aspeed_espi_vw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aspeed_espi_vw_ioctl, +}; + +static void aspeed_espi_vw_event(uint32_t sts, struct aspeed_espi_vw *espi_vw) +{ + uint32_t sysevt_sts; + struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl; + + regmap_read(espi_ctrl->map, ESPI_INT_STS, &sts); + + if (sts & ESPI_INT_STS_VW_SYSEVT) { + regmap_read(espi_ctrl->map, ESPI_SYSEVT_INT_STS, &sysevt_sts); + + if (espi_ctrl->version == ESPI_AST2500) { + if (sysevt_sts & ESPI_SYSEVT_INT_STS_HOST_RST_WARN) + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT, + ESPI_SYSEVT_HOST_RST_ACK, + ESPI_SYSEVT_HOST_RST_ACK); + + if (sysevt_sts & ESPI_SYSEVT_INT_STS_OOB_RST_WARN) + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT, + ESPI_SYSEVT_OOB_RST_ACK, + ESPI_SYSEVT_OOB_RST_ACK); + } + + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_STS, sysevt_sts); + } + + if (sts & ESPI_INT_STS_VW_SYSEVT1) { + regmap_read(espi_ctrl->map, ESPI_SYSEVT1_INT_STS, &sysevt_sts); + + if (sysevt_sts & ESPI_SYSEVT1_INT_STS_SUSPEND_WARN) + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT1, + ESPI_SYSEVT1_SUSPEND_ACK, + ESPI_SYSEVT1_SUSPEND_ACK); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_STS, sysevt_sts); + } +} + +static void aspeed_espi_vw_enable(struct aspeed_espi_vw *espi_vw) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl; + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_VW_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_VW_BITS, + ESPI_INT_EN_VW_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_VW_SW_RDY, + ESPI_CTRL_VW_SW_RDY); +} + +static void *aspeed_espi_vw_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc; + struct aspeed_espi_vw *espi_vw; + + espi_vw = devm_kzalloc(dev, sizeof(*espi_vw), GFP_KERNEL); + if (!espi_vw) + return ERR_PTR(-ENOMEM); + + espi_vw->ctrl = espi_ctrl; + + espi_vw->mdev.parent = dev; + espi_vw->mdev.minor = MISC_DYNAMIC_MINOR; + espi_vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", VW_MDEV_NAME); + espi_vw->mdev.fops = &aspeed_espi_vw_fops; + rc = misc_register(&espi_vw->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_vw_enable(espi_vw); + + return espi_vw; +} + +static void aspeed_espi_vw_free(struct device *dev, struct aspeed_espi_vw *espi_vw) +{ + misc_deregister(&espi_vw->mdev); +} + +#endif -- 2.17.1 From lkp at intel.com Fri Aug 20 03:27:30 2021 From: lkp at intel.com (kernel test robot) Date: Fri, 20 Aug 2021 01:27:30 +0800 Subject: [PATCH v2 4/5] soc: aspeed: Add eSPI driver In-Reply-To: <20210819080040.31242-5-chiawei_wang@aspeedtech.com> References: <20210819080040.31242-5-chiawei_wang@aspeedtech.com> Message-ID: <202108200149.MsDMcZt1-lkp@intel.com> Hi Chia-Wei, I love your patch! Yet something to improve: [auto build test ERROR on robh/for-next] [also build test ERROR on arm/for-next keystone/next soc/for-next rockchip/for-next arm64/for-next/core linus/master joel-aspeed/for-next v5.14-rc6 next-20210819] [cannot apply to xlnx/master] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210819-160303 base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next config: sh-allmodconfig (attached as .config) compiler: sh4-linux-gcc (GCC) 11.2.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/cd398492dc8a9d27e1f7b546f1f4df371f058566 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210819-160303 git checkout cd398492dc8a9d27e1f7b546f1f4df371f058566 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=sh If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot All error/warnings (new ones prefixed by >>): In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:21: drivers/soc/aspeed/aspeed-espi-perif.h: In function 'aspeed_espi_perif_pc_get_rx': >> drivers/soc/aspeed/aspeed-espi-perif.h:106:15: error: implicit declaration of function 'vmalloc'; did you mean 'kvmalloc'? [-Werror=implicit-function-declaration] 106 | pkt = vmalloc(pkt_len); | ^~~~~~~ | kvmalloc >> drivers/soc/aspeed/aspeed-espi-perif.h:106:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 106 | pkt = vmalloc(pkt_len); | ^ drivers/soc/aspeed/aspeed-espi-perif.h: In function 'aspeed_espi_perif_pc_put_tx': drivers/soc/aspeed/aspeed-espi-perif.h:163:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 163 | pkt = vmalloc(ioc->pkt_len); | ^ >> drivers/soc/aspeed/aspeed-espi-perif.h:202:9: error: implicit declaration of function 'vfree'; did you mean 'kfree'? [-Werror=implicit-function-declaration] 202 | vfree(pkt); | ^~~~~ | kfree drivers/soc/aspeed/aspeed-espi-perif.h: In function 'aspeed_espi_perif_np_put_tx': drivers/soc/aspeed/aspeed-espi-perif.h:230:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 230 | pkt = vmalloc(ioc->pkt_len); | ^ In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:23: drivers/soc/aspeed/aspeed-espi-oob.h: In function 'aspeed_espi_oob_dma_desc_get_rx': >> drivers/soc/aspeed/aspeed-espi-oob.h:106:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 106 | pkt = vmalloc(pkt_len); | ^ drivers/soc/aspeed/aspeed-espi-oob.h: In function 'aspeed_espi_oob_get_rx': drivers/soc/aspeed/aspeed-espi-oob.h:203:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 203 | pkt = vmalloc(pkt_len); | ^ drivers/soc/aspeed/aspeed-espi-oob.h: In function 'aspeed_espi_oob_dma_desc_put_tx': >> drivers/soc/aspeed/aspeed-espi-oob.h:262:15: error: implicit declaration of function 'vzalloc'; did you mean 'kvzalloc'? [-Werror=implicit-function-declaration] 262 | pkt = vzalloc(ioc->pkt_len); | ^~~~~~~ | kvzalloc drivers/soc/aspeed/aspeed-espi-oob.h:262:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 262 | pkt = vzalloc(ioc->pkt_len); | ^ drivers/soc/aspeed/aspeed-espi-oob.h: In function 'aspeed_espi_oob_put_tx': drivers/soc/aspeed/aspeed-espi-oob.h:334:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 334 | pkt = vmalloc(ioc->pkt_len); | ^ In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:24: drivers/soc/aspeed/aspeed-espi-flash.h: In function 'aspeed_espi_flash_get_rx': >> drivers/soc/aspeed/aspeed-espi-flash.h:119:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 119 | pkt = vmalloc(pkt_len); | ^ drivers/soc/aspeed/aspeed-espi-flash.h: In function 'aspeed_espi_flash_put_tx': drivers/soc/aspeed/aspeed-espi-flash.h:186:13: warning: assignment to 'uint8_t *' {aka 'unsigned char *'} from 'int' makes pointer from integer without a cast [-Wint-conversion] 186 | pkt = vmalloc(ioc->pkt_len); | ^ drivers/soc/aspeed/aspeed-espi-ctrl.c: In function 'aspeed_espi_ctrl_probe': drivers/soc/aspeed/aspeed-espi-ctrl.c:93:24: warning: unused variable 'scu' [-Wunused-variable] 93 | struct regmap *scu; | ^~~ drivers/soc/aspeed/aspeed-espi-ctrl.c:90:18: warning: unused variable 'reg' [-Wunused-variable] 90 | uint32_t reg; | ^~~ cc1: some warnings being treated as errors Kconfig warnings: (for reference only) WARNING: unmet direct dependencies detected for SND_ATMEL_SOC_PDC Depends on SOUND && !UML && SND && SND_SOC && SND_ATMEL_SOC && HAS_DMA Selected by - SND_ATMEL_SOC_SSC && SOUND && !UML && SND && SND_SOC && SND_ATMEL_SOC - SND_ATMEL_SOC_SSC_PDC && SOUND && !UML && SND && SND_SOC && SND_ATMEL_SOC && ATMEL_SSC vim +106 drivers/soc/aspeed/aspeed-espi-perif.h 46 47 static long aspeed_espi_perif_pc_get_rx(struct file *fp, 48 struct aspeed_espi_ioc *ioc, 49 struct aspeed_espi_perif *espi_perif) 50 { 51 int i, rc; 52 uint32_t reg; 53 uint32_t cyc, tag, len; 54 uint8_t *pkt; 55 uint32_t pkt_len; 56 struct espi_comm_hdr *hdr; 57 unsigned long flags; 58 struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; 59 60 if (!espi_perif->rx_ready) { 61 if (fp->f_flags & O_NONBLOCK) 62 return -ENODATA; 63 64 rc = wait_event_interruptible(espi_perif->wq, espi_perif->rx_ready); 65 if (rc == -ERESTARTSYS) 66 return -EINTR; 67 } 68 69 /* common header (i.e. cycle type, tag, and length) is taken by HW */ 70 regmap_read(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL, ®); 71 cyc = (reg & ESPI_PERIF_PC_RX_CTRL_CYC_MASK) >> ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT; 72 tag = (reg & ESPI_PERIF_PC_RX_CTRL_TAG_MASK) >> ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT; 73 len = (reg & ESPI_PERIF_PC_RX_CTRL_LEN_MASK) >> ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT; 74 75 /* 76 * calculate the length of the rest part of the 77 * eSPI packet to be read from HW and copied to 78 * user space. 79 */ 80 switch (cyc) { 81 case ESPI_PERIF_MSG: 82 pkt_len = len + sizeof(struct espi_perif_msg); 83 break; 84 case ESPI_PERIF_MSG_D: 85 pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + 86 sizeof(struct espi_perif_msg); 87 break; 88 case ESPI_PERIF_SUC_CMPLT_D_MIDDLE: 89 case ESPI_PERIF_SUC_CMPLT_D_FIRST: 90 case ESPI_PERIF_SUC_CMPLT_D_LAST: 91 case ESPI_PERIF_SUC_CMPLT_D_ONLY: 92 pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + 93 sizeof(struct espi_perif_cmplt); 94 break; 95 case ESPI_PERIF_SUC_CMPLT: 96 case ESPI_PERIF_UNSUC_CMPLT: 97 pkt_len = len + sizeof(struct espi_perif_cmplt); 98 break; 99 default: 100 return -EFAULT; 101 } 102 103 if (ioc->pkt_len < pkt_len) 104 return -EINVAL; 105 > 106 pkt = vmalloc(pkt_len); 107 if (!pkt) 108 return -ENOMEM; 109 110 hdr = (struct espi_comm_hdr *)pkt; 111 hdr->cyc = cyc; 112 hdr->tag = tag; 113 hdr->len_h = len >> 8; 114 hdr->len_l = len & 0xff; 115 116 if (espi_perif->dma_mode) { 117 memcpy(hdr + 1, espi_perif->dma.pc_rx_virt, 118 pkt_len - sizeof(*hdr)); 119 } else { 120 for (i = sizeof(*hdr); i < pkt_len; ++i) { 121 regmap_read(espi_ctrl->map, 122 ESPI_PERIF_PC_RX_PORT, ®); 123 pkt[i] = reg & 0xff; 124 } 125 } 126 127 if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) 128 return -EFAULT; 129 130 spin_lock_irqsave(&espi_perif->rx_lock, flags); 131 132 regmap_write_bits(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL, 133 ESPI_PERIF_PC_RX_CTRL_PEND_SERV, 134 ESPI_PERIF_PC_RX_CTRL_PEND_SERV); 135 136 espi_perif->rx_ready = 0; 137 138 spin_unlock_irqrestore(&espi_perif->rx_lock, flags); 139 140 return pkt_len; 141 } 142 143 static long aspeed_espi_perif_pc_put_tx(struct file *fp, 144 struct aspeed_espi_ioc *ioc, 145 struct aspeed_espi_perif *espi_perif) 146 { 147 int i, rc = 0; 148 uint32_t reg; 149 uint32_t cyc, tag, len; 150 uint8_t *pkt; 151 struct espi_comm_hdr *hdr; 152 struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; 153 154 if (!mutex_trylock(&espi_perif->pc_tx_lock)) 155 return -EAGAIN; 156 157 regmap_read(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, ®); 158 if (reg & ESPI_PERIF_PC_TX_CTRL_TRIGGER) { 159 rc = -EBUSY; 160 goto unlock_n_out; 161 } 162 163 pkt = vmalloc(ioc->pkt_len); 164 if (!pkt) { 165 rc = -ENOMEM; 166 goto unlock_n_out; 167 } 168 169 hdr = (struct espi_comm_hdr *)pkt; 170 171 if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { 172 rc = -EFAULT; 173 goto free_n_out; 174 } 175 176 /* 177 * common header (i.e. cycle type, tag, and length) 178 * part is written to HW registers 179 */ 180 if (espi_perif->dma_mode) { 181 memcpy(espi_perif->dma.pc_tx_virt, hdr + 1, 182 ioc->pkt_len - sizeof(*hdr)); 183 dma_wmb(); 184 } else { 185 for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) 186 regmap_write(espi_ctrl->map, 187 ESPI_PERIF_PC_TX_PORT, pkt[i]); 188 } 189 190 cyc = hdr->cyc; 191 tag = hdr->tag; 192 len = (hdr->len_h << 8) | (hdr->len_l & 0xff); 193 194 reg = ((cyc << ESPI_PERIF_PC_TX_CTRL_CYC_SHIFT) & ESPI_PERIF_PC_TX_CTRL_CYC_MASK) 195 | ((tag << ESPI_PERIF_PC_TX_CTRL_TAG_SHIFT) & ESPI_PERIF_PC_TX_CTRL_TAG_MASK) 196 | ((len << ESPI_PERIF_PC_TX_CTRL_LEN_SHIFT) & ESPI_PERIF_PC_TX_CTRL_LEN_MASK) 197 | ESPI_PERIF_PC_TX_CTRL_TRIGGER; 198 199 regmap_write(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, reg); 200 201 free_n_out: > 202 vfree(pkt); 203 204 unlock_n_out: 205 mutex_unlock(&espi_perif->pc_tx_lock); 206 207 return rc; 208 } 209 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org -------------- next part -------------- A non-text attachment was scrubbed... Name: .config.gz Type: application/gzip Size: 55035 bytes Desc: not available URL: From chiawei_wang at aspeedtech.com Mon Aug 23 11:21:15 2021 From: chiawei_wang at aspeedtech.com (ChiaWei Wang) Date: Mon, 23 Aug 2021 01:21:15 +0000 Subject: [PATCH v2 1/5] dt-bindings: aspeed: Add eSPI controller In-Reply-To: References: <20210819080040.31242-1-chiawei_wang@aspeedtech.com> <20210819080040.31242-2-chiawei_wang@aspeedtech.com> Message-ID: Hi Rob, > -----Original Message----- > From: Rob Herring > Sent: Saturday, August 21, 2021 4:02 AM > To: ChiaWei Wang > Cc: joel at jms.id.au; andrew at aj.id.au; linux-aspeed at lists.ozlabs.org; > openbmc at lists.ozlabs.org; devicetree at vger.kernel.org; > linux-arm-kernel at lists.infradead.org; linux-kernel at vger.kernel.org; Ryan Chen > > Subject: Re: [PATCH v2 1/5] dt-bindings: aspeed: Add eSPI controller > > On Thu, Aug 19, 2021 at 04:00:36PM +0800, Chia-Wei Wang wrote: > > Add dt-bindings for Aspeed eSPI controller > > > > Signed-off-by: Chia-Wei Wang > > --- > > .../devicetree/bindings/soc/aspeed/espi.yaml | 158 > > ++++++++++++++++++ > > 1 file changed, 158 insertions(+) > > create mode 100644 > > Documentation/devicetree/bindings/soc/aspeed/espi.yaml > > > > diff --git a/Documentation/devicetree/bindings/soc/aspeed/espi.yaml > > b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml > > new file mode 100644 > > index 000000000000..fec3d37f3ffd > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml > > @@ -0,0 +1,158 @@ > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) # # > > +Copyright (c) 2021 Aspeed Technology Inc. > > +%YAML 1.2 > > +--- > > +$id: "http://devicetree.org/schemas/soc/aspeed/espi.yaml#" > > +$schema: "http://devicetree.org/meta-schemas/core.yaml#" > > + > > +title: Aspeed eSPI Controller > > + > > +maintainers: > > + - Chia-Wei Wang > > + - Ryan Chen > > + > > +description: > > + Aspeed eSPI controller implements a slave side eSPI endpoint device > > + supporting the four eSPI channels, namely peripheral, virtual wire, > > + out-of-band, and flash. > > + > > +properties: > > + compatible: > > + items: > > + - enum: > > + - aspeed,ast2500-espi > > + - aspeed,ast2600-espi > > + - const: simple-mfd > > + - const: syscon > > Is this really 2 sub devices that could be used individually or in a different > combination? If not, then I'd make all this 1 node. espi-mmbi has individual function and control registers. However, espi-mmbi is also a feature extended based on the memory cycle of eSPI peripheral channel. Thereby, it has dependency on the eSPI channel initialization conducted by espi-ctrl. The scenario is similar to the lpc-ctrl and other lpc-xxx drivers of Aspeed SoCs. Chiawei From billy_tsai at aspeedtech.com Mon Aug 23 17:02:26 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:26 +0800 Subject: [v4 01/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-2-billy_tsai@aspeedtech.com> Add device tree bindings document for the aspeed ast2600 adc device driver. Signed-off-by: Billy Tsai --- .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml new file mode 100644 index 000000000000..248cda7d91e9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC that forms part of an ASPEED server management processor. + +maintainers: + - Billy Tsai + +description: | + ? 10-bits resolution for 16 voltage channels. + ? The device split into two individual engine and each contains 8 voltage + channels. + ? Channel scanning can be non-continuous. + ? Programmable ADC clock frequency. + ? Programmable upper and lower threshold for each channels. + ? Interrupt when larger or less than threshold for each channels. + ? Support hysteresis for each channels. + ? Built-in a compensating method. + ? Built-in a register to trim internal reference voltage. + ? Internal or External reference voltage. + ? Support 2 Internal reference voltage 1.2v or 2.5v. + ? Integrate dividing circuit for battery sensing. + +properties: + compatible: + enum: + - aspeed,ast2600-adc0 + - aspeed,ast2600-adc1 + + reg: + maxItems: 1 + + clocks: + description: + Input clock used to derive the sample clock. Expected to be the + SoC's APB clock. + + resets: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + vref-supply: + description: + The external regulator supply ADC reference voltage. + + aspeed,int_vref_mv: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1200, 2500] + description: + ADC internal reference voltage in millivolts. + + aspeed,battery-sensing: + type: boolean + description: + Inform the driver that last channel will be used to sensor battery. + + aspeed,trim-data-valid: + type: boolean + description: | + The ADC reference voltage can be calibrated to obtain the trimming + data which will be stored in otp. This property informs the driver that + the data store in the otp is valid. + +required: + - compatible + - reg + - clocks + - resets + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include + adc0: adc at 1e6e9000 { + compatible = "aspeed,ast2600-adc0"; + reg = <0x1e6e9000 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int_vref_mv = <2500>; + }; + adc1: adc at 1e6e9100 { + compatible = "aspeed,ast2600-adc1"; + reg = <0x1e6e9100 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int_vref_mv = <2500>; + }; +... -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:32 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:32 +0800 Subject: [v4 07/15] iio: adc: aspeed: Set num_channels with model data In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-8-billy_tsai@aspeedtech.com> Use the model_data member num_channels to set the num_channels of iio device. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index f260e40ab9b2..2d6215a91f99 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -291,7 +291,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); + indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); if (ret) -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:34 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:34 +0800 Subject: [v4 09/15] iio: adc: aspeed: Use devm_add_action_or_reset. In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-10-billy_tsai@aspeedtech.com> This patch use devm_add_action_or_reset to handle the error in probe phase. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 92 +++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 52db38be9699..1c87e12a0cab 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -187,6 +187,27 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_unregister_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_divider(clk); +} + +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -232,6 +253,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) &data->clk_lock); if (IS_ERR(data->clk_prescaler)) return PTR_ERR(data->clk_prescaler); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_divider, + data->clk_prescaler); + if (ret) + return ret; snprintf(clk_parent_name, 32, clk_name); scaler_flags = CLK_SET_RATE_PARENT; } @@ -244,23 +271,30 @@ static int aspeed_adc_probe(struct platform_device *pdev) &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, data->model_data->scaler_bit_width, 0, &data->clk_lock); - if (IS_ERR(data->clk_scaler)) { - ret = PTR_ERR(data->clk_scaler); - goto scaler_error; - } + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); + + ret = devm_add_action_or_reset(data->dev, aspeed_adc_unregister_divider, + data->clk_scaler); + if (ret) + return ret; data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); - ret = PTR_ERR(data->rst); - goto reset_error; + return PTR_ERR(data->rst); } reset_control_deassert(data->rst); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, + data->rst); + if (ret) + return ret; + ret = aspeed_adc_vref_config(indio_dev); if (ret) - goto vref_config_error; + return ret; if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ @@ -277,13 +311,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_INIT_POLLING_TIME, ASPEED_ADC_INIT_TIMEOUT); if (ret) - goto poll_timeout_error; + return ret; } /* Start all channels in normal mode. */ ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) - goto clk_enable_error; + return ret; + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; adc_engine_control_reg_val = ASPEED_ADC_CTRL_CHANNEL | @@ -299,41 +339,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); - if (ret) - goto iio_register_error; - + if (ret) { + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); + return ret; + } return 0; - -iio_register_error: - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); -clk_enable_error: -poll_timeout_error: -vref_config_error: - reset_control_assert(data->rst); -reset_error: - clk_hw_unregister_divider(data->clk_scaler); -scaler_error: - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return ret; } static int aspeed_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct aspeed_adc_data *data = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); - reset_control_assert(data->rst); - clk_hw_unregister_divider(data->clk_scaler); - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return 0; } -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:25 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:25 +0800 Subject: [v4 00/15] Add support for ast2600 ADC Message-ID: <20210823070240.12600-1-billy_tsai@aspeedtech.com> This patch serials make aspeed_adc.c can support ast2600 and backward compatible. Change since v3: dt-bindings: - Fix properties:aspeed,int_vref_mv type error. Change since v2: dt-bindings: - Create a new dt-bindings for ast2600 adc aspeed_adc.c: - Splits the patch for more details - Remove version enum and use the flags in model data to distinguish hardware feature - Support trimming data get and set. - Use devm_add_action_or_reset to simplify probe error handling. Changes since v1: dt-bindings: - Fix the aspeed,adc.yaml check error. - Add battery-sensing property. aspeed_adc.c: - Change the init flow: Clock and reference voltage setting should be completed before adc engine enable. - Change the default sampling rate to meet most user case. - Add patch #8 to suppoert battery sensing mode. Billy Tsai (15): dt-bindings: iio: adc: Add ast2600-adc bindings iio: adc: aspeed: completes the bitfield declare. iio: adc: aspeed: set driver data when adc probe. iio: adc: aspeed: Keep model data to driver data. iio: adc: aspeed: Refactory model data structure iio: adc: aspeed: Add vref config function iio: adc: aspeed: Set num_channels with model data iio: adc: aspeed: Use model_data to set clk scaler. iio: adc: aspeed: Use devm_add_action_or_reset. iio: adc: aspeed: Support ast2600 adc. iio: adc: aspeed: Fix the calculate error of clock. iio: adc: aspeed: Add func to set sampling rate. iio: adc: aspeed: Add compensation phase. iio: adc: aspeed: Support battery sensing. iio: adc: aspeed: Get and set trimming data. .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 97 +++ drivers/iio/adc/aspeed_adc.c | 562 +++++++++++++++--- 2 files changed, 569 insertions(+), 90 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:27 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:27 +0800 Subject: [v4 02/15] iio: adc: aspeed: completes the bitfield declare. In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-3-billy_tsai@aspeedtech.com> This patch completes the declare of adc register bitfields and uses the same prefix ASPEED_ADC_* for these bitfields. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 56 +++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 19efaa41bc34..7010d56ac3b9 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -3,6 +3,7 @@ * Aspeed AST2400/2500 ADC * * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2021 Aspeed Technology Inc. */ #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,15 +30,31 @@ #define ASPEED_REG_INTERRUPT_CONTROL 0x04 #define ASPEED_REG_VGA_DETECT_CONTROL 0x08 #define ASPEED_REG_CLOCK_CONTROL 0x0C -#define ASPEED_REG_MAX 0xC0 - -#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) -#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) -#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) - -#define ASPEED_ENGINE_ENABLE BIT(0) - -#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 +#define ASPEED_REG_MAX 0xCC + +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 +#define ASPEED_ADC_OP_MODE_STANDBY 1 +#define ASPEED_ADC_OP_MODE_NORMAL 7 +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_ADC_CH7_MODE BIT(12) +#define ASPEED_ADC_CH7_NORMAL 0 +#define ASPEED_ADC_CH7_BAT 1 +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 @@ -226,7 +244,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (model_data->wait_init_sequence) { /* Enable engine in normal mode. */ - writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); /* Wait for initial sequence complete. */ @@ -245,10 +265,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) goto clk_enable_error; - adc_engine_control_reg_val = GENMASK(31, 16) | - ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + adc_engine_control_reg_val = + ASPEED_ADC_CTRL_CHANNEL | + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); + data->base + ASPEED_REG_ENGINE_CONTROL); model_data = of_device_get_match_data(&pdev->dev); indio_dev->name = model_data->model_name; @@ -264,8 +286,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) return 0; iio_register_error: - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: @@ -283,8 +305,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) struct aspeed_adc_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:33 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:33 +0800 Subject: [v4 08/15] iio: adc: aspeed: Use model_data to set clk scaler. In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-9-billy_tsai@aspeedtech.com> This patch use need_prescaler and scaler_bit_width to set the adc clock scaler. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 39 +++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 2d6215a91f99..52db38be9699 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -202,9 +202,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; + unsigned long scaler_flags = 0; + char clk_name[32], clk_parent_name[32]; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); if (!indio_dev) @@ -221,24 +222,28 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Register ADC clock prescaler with source specified by device tree. */ spin_lock_init(&data->clk_lock); - clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); - - data->clk_prescaler = clk_hw_register_divider( - &pdev->dev, "prescaler", clk_parent_name, 0, - data->base + ASPEED_REG_CLOCK_CONTROL, - 17, 15, 0, &data->clk_lock); - if (IS_ERR(data->clk_prescaler)) - return PTR_ERR(data->clk_prescaler); - + snprintf(clk_parent_name, 32, of_clk_get_parent_name(pdev->dev.of_node, 0)); + if (data->model_data->need_prescaler) { + snprintf(clk_name, 32, "%s-prescaler", + data->model_data->model_name); + data->clk_prescaler = clk_hw_register_divider( + &pdev->dev, clk_name, clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, + &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + snprintf(clk_parent_name, 32, clk_name); + scaler_flags = CLK_SET_RATE_PARENT; + } /* * Register ADC clock scaler downstream from the prescaler. Allow rate * setting to adjust the prescaler as well. */ + snprintf(clk_name, 32, "%s-scaler", data->model_data->model_name); data->clk_scaler = clk_hw_register_divider( - &pdev->dev, "scaler", "prescaler", - CLK_SET_RATE_PARENT, - data->base + ASPEED_REG_CLOCK_CONTROL, - 0, 10, 0, &data->clk_lock); + &pdev->dev, clk_name, clk_parent_name, scaler_flags, + data->base + ASPEED_REG_CLOCK_CONTROL, 0, + data->model_data->scaler_bit_width, 0, &data->clk_lock); if (IS_ERR(data->clk_scaler)) { ret = PTR_ERR(data->clk_scaler); goto scaler_error; @@ -310,7 +315,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) reset_error: clk_hw_unregister_divider(data->clk_scaler); scaler_error: - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return ret; } @@ -325,7 +331,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return 0; } -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:28 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:28 +0800 Subject: [v4 03/15] iio: adc: aspeed: set driver data when adc probe. In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-4-billy_tsai@aspeedtech.com> Fix the issue when adc remove will get the null driver data. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 7010d56ac3b9..20462cf659e4 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -201,6 +201,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:29 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:29 +0800 Subject: [v4 04/15] iio: adc: aspeed: Keep model data to driver data. In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-5-billy_tsai@aspeedtech.com> Keep the model data pointer to driver data for reducing the usage of of_device_get_match_data(). Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 20462cf659e4..d85aa31ee3b1 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -69,6 +69,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; + const struct aspeed_adc_model_data *model_data; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -110,8 +111,6 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -119,7 +118,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = model_data->vref_voltage; + *val = data->model_data->vref_voltage; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -138,13 +137,11 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < model_data->min_sampling_rate || - val > model_data->max_sampling_rate) + if (val < data->model_data->min_sampling_rate || + val > data->model_data->max_sampling_rate) return -EINVAL; clk_set_rate(data->clk_scaler->clk, @@ -190,7 +187,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const struct aspeed_adc_model_data *model_data; const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; @@ -201,6 +197,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + data->model_data = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); @@ -241,9 +238,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); - model_data = of_device_get_match_data(&pdev->dev); - - if (model_data->wait_init_sequence) { + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | @@ -273,8 +268,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); - model_data = of_device_get_match_data(&pdev->dev); - indio_dev->name = model_data->model_name; + indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:30 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:30 +0800 Subject: [v4 05/15] iio: adc: aspeed: Refactory model data structure In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-6-billy_tsai@aspeedtech.com> This patch refactory the model data structure to distinguish the function form differnet version of aspeed adc. - Rename the vref_voltag to vref_fixed and add vref driver data When driver probe will check vref_fixed value and store it to vref which isn't const value. - Add num_channels Make num_channles of iio device can be changed by differnet model_data - Add need_prescaler flag and scaler_bit_width The need_prescaler flag used to tell the driver the clock divider needs another prescaler and the scaler_bit_width to set the clock divider bitfiled width. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index d85aa31ee3b1..f03c7921d534 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -63,8 +63,11 @@ struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz unsigned int max_sampling_rate; // Hz - unsigned int vref_voltage; // mV + unsigned int vref_fixed; // mV bool wait_init_sequence; + bool need_prescaler; + u8 scaler_bit_width; + unsigned int num_channels; }; struct aspeed_adc_data { @@ -75,6 +78,7 @@ struct aspeed_adc_data { struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; + int vref; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -118,7 +122,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_voltage; + *val = data->model_data->vref_fixed; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -312,17 +316,23 @@ static int aspeed_adc_remove(struct platform_device *pdev) static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", - .vref_voltage = 2500, // mV + .vref_fixed = 2500, // mV .min_sampling_rate = 10000, .max_sampling_rate = 500000, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct aspeed_adc_model_data ast2500_model_data = { .model_name = "ast2500-adc", - .vref_voltage = 1800, // mV + .vref_fixed = 1800, // mV .min_sampling_rate = 1, .max_sampling_rate = 1000000, .wait_init_sequence = true, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct of_device_id aspeed_adc_matches[] = { -- 2.25.1 From billy_tsai at aspeedtech.com Mon Aug 23 17:02:31 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 23 Aug 2021 15:02:31 +0800 Subject: [v4 06/15] iio: adc: aspeed: Add vref config function In-Reply-To: <20210823070240.12600-1-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> Message-ID: <20210823070240.12600-7-billy_tsai@aspeedtech.com> Add the function to check the vref_fixed and set the value to driver data. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index f03c7921d534..f260e40ab9b2 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -122,7 +122,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_fixed; + *val = data->vref; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -187,6 +187,17 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static int aspeed_adc_vref_config(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (data->model_data->vref_fixed) { + data->vref = data->model_data->vref_fixed; + return 0; + } + return 0; +} + static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -242,6 +253,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); + ret = aspeed_adc_vref_config(indio_dev); + if (ret) + goto vref_config_error; + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, @@ -290,6 +305,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: +vref_config_error: reset_control_assert(data->rst); reset_error: clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From caihuoqing at baidu.com Mon Aug 23 14:58:07 2021 From: caihuoqing at baidu.com (Cai Huoqing) Date: Mon, 23 Aug 2021 12:58:07 +0800 Subject: [PATCH] usb: gadget: aspeed: Remove repeated verbose license text Message-ID: <20210823045807.49-1-caihuoqing@baidu.com> remove it because SPDX-License-Identifier is already used Signed-off-by: Cai Huoqing --- drivers/usb/gadget/udc/aspeed-vhub/core.c | 5 ----- drivers/usb/gadget/udc/aspeed-vhub/dev.c | 5 ----- drivers/usb/gadget/udc/aspeed-vhub/ep0.c | 5 ----- drivers/usb/gadget/udc/aspeed-vhub/epn.c | 5 ----- drivers/usb/gadget/udc/aspeed-vhub/hub.c | 5 ----- 5 files changed, 25 deletions(-) diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c index d11d3d14313f..7a635c499777 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/core.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c @@ -5,11 +5,6 @@ * core.c - Top level support * * Copyright 2017 IBM Corporation - * - * 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 diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c index d268306a7bfe..d918e8b2af3c 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c @@ -5,11 +5,6 @@ * dev.c - Individual device/gadget management (ie, a port = a gadget) * * Copyright 2017 IBM Corporation - * - * 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 diff --git a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c index 022b777b85f8..74ea36c19b1e 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/ep0.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/ep0.c @@ -5,11 +5,6 @@ * ep0.c - Endpoint 0 handling * * Copyright 2017 IBM Corporation - * - * 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 diff --git a/drivers/usb/gadget/udc/aspeed-vhub/epn.c b/drivers/usb/gadget/udc/aspeed-vhub/epn.c index cb164c615e6f..917892ca8753 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/epn.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/epn.c @@ -5,11 +5,6 @@ * epn.c - Generic endpoints management * * Copyright 2017 IBM Corporation - * - * 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 diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index 5c7dea5e0ff1..b9960fdd8a51 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -5,11 +5,6 @@ * hub.c - virtual hub handling * * Copyright 2017 IBM Corporation - * - * 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 -- 2.25.1 From joel at jms.id.au Tue Aug 24 14:52:13 2021 From: joel at jms.id.au (Joel Stanley) Date: Tue, 24 Aug 2021 04:52:13 +0000 Subject: [PATCH -next] soc: aspeed-lpc-ctrl: Fix missing clk_disable_unprepare() on error in aspeed_lpc_ctrl_probe() In-Reply-To: <20210823130043.1087204-1-yangyingliang@huawei.com> References: <20210823130043.1087204-1-yangyingliang@huawei.com> Message-ID: On Mon, 23 Aug 2021 at 12:55, Yang Yingliang wrote: > > Fix the missing clk_disable_unprepare() before return > from aspeed_lpc_ctrl_probe() in the error handling case. > > Fixes: 2f9b25fa6682 ("soc: aspeed: Re-enable FWH2AHB on AST2600") > Reported-by: Hulk Robot > Signed-off-by: Yang Yingliang > --- > drivers/soc/aspeed/aspeed-lpc-ctrl.c | 3 ++- > 1 file changed, 2 insertions(+), 1 deletion(-) > > diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c > index 6893c5ec3259..f4c989584d6b 100644 > --- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c > +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c > @@ -312,7 +312,8 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) > lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); > if (IS_ERR(lpc_ctrl->scu)) { > dev_err(dev, "couldn't find scu\n"); > - return PTR_ERR(lpc_ctrl->scu); > + rc = PTR_ERR(lpc_ctrl->scu); > + goto err; Thanks for the patch. Alternatively, we could move this code above the devm_clk_get/clk_prepare_enable. I would prefer that option, if you want to send a v2. Cheers, Joel From yangyingliang at huawei.com Tue Aug 24 17:39:47 2021 From: yangyingliang at huawei.com (Yang Yingliang) Date: Tue, 24 Aug 2021 15:39:47 +0800 Subject: [PATCH -next] soc: aspeed-lpc-ctrl: Fix missing clk_disable_unprepare() on error in aspeed_lpc_ctrl_probe() In-Reply-To: References: <20210823130043.1087204-1-yangyingliang@huawei.com> Message-ID: <7ee18b90-f877-15cd-e8aa-c8024847f924@huawei.com> Hi, On 2021/8/24 12:52, Joel Stanley wrote: > On Mon, 23 Aug 2021 at 12:55, Yang Yingliang wrote: >> Fix the missing clk_disable_unprepare() before return >> from aspeed_lpc_ctrl_probe() in the error handling case. >> >> Fixes: 2f9b25fa6682 ("soc: aspeed: Re-enable FWH2AHB on AST2600") >> Reported-by: Hulk Robot >> Signed-off-by: Yang Yingliang >> --- >> drivers/soc/aspeed/aspeed-lpc-ctrl.c | 3 ++- >> 1 file changed, 2 insertions(+), 1 deletion(-) >> >> diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c >> index 6893c5ec3259..f4c989584d6b 100644 >> --- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c >> +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c >> @@ -312,7 +312,8 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) >> lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); >> if (IS_ERR(lpc_ctrl->scu)) { >> dev_err(dev, "couldn't find scu\n"); >> - return PTR_ERR(lpc_ctrl->scu); >> + rc = PTR_ERR(lpc_ctrl->scu); >> + goto err; > Thanks for the patch. Alternatively, we could move this code above the > devm_clk_get/clk_prepare_enable. I would prefer that option, if you > want to send a v2. I can send a v2. Thanks, Yang > > Cheers, > > Joel > . From yangyingliang at huawei.com Tue Aug 24 18:55:22 2021 From: yangyingliang at huawei.com (Yang Yingliang) Date: Tue, 24 Aug 2021 16:55:22 +0800 Subject: [PATCH -next v2] soc: aspeed-lpc-ctrl: Fix missing clk_disable_unprepare() on error in aspeed_lpc_ctrl_probe() Message-ID: <20210824085522.1849410-1-yangyingliang@huawei.com> Move syscon_regmap_lookup_by_compatible() above the devm_clk_get() to avoid missing clk_disable_unprepare(). Fixes: 2f9b25fa6682 ("soc: aspeed: Re-enable FWH2AHB on AST2600") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang --- drivers/soc/aspeed/aspeed-lpc-ctrl.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c index 6893c5ec3259..72771e018c42 100644 --- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c @@ -295,6 +295,16 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) return -ENODEV; } + if (of_device_is_compatible(dev->of_node, "aspeed,ast2600-lpc-ctrl")) { + lpc_ctrl->fwh2ahb = true; + + lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); + if (IS_ERR(lpc_ctrl->scu)) { + dev_err(dev, "couldn't find scu\n"); + return PTR_ERR(lpc_ctrl->scu); + } + } + lpc_ctrl->clk = devm_clk_get(dev, NULL); if (IS_ERR(lpc_ctrl->clk)) { dev_err(dev, "couldn't get clock\n"); @@ -306,16 +316,6 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) return rc; } - if (of_device_is_compatible(dev->of_node, "aspeed,ast2600-lpc-ctrl")) { - lpc_ctrl->fwh2ahb = true; - - lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); - if (IS_ERR(lpc_ctrl->scu)) { - dev_err(dev, "couldn't find scu\n"); - return PTR_ERR(lpc_ctrl->scu); - } - } - lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; lpc_ctrl->miscdev.name = DEVICE_NAME; lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:35 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:35 +0800 Subject: [RESEND v4 07/15] iio: adc: aspeed: Set num_channels with model data In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-8-billy_tsai@aspeedtech.com> Use the model_data member num_channels to set the num_channels of iio device. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index f260e40ab9b2..2d6215a91f99 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -291,7 +291,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); + indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); if (ret) -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:30 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:30 +0800 Subject: [RESEND v4 02/15] iio: adc: aspeed: completes the bitfield declare. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-3-billy_tsai@aspeedtech.com> This patch completes the declare of adc register bitfields and uses the same prefix ASPEED_ADC_* for these bitfields. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 56 +++++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 19efaa41bc34..7010d56ac3b9 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -3,6 +3,7 @@ * Aspeed AST2400/2500 ADC * * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2021 Aspeed Technology Inc. */ #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,15 +30,31 @@ #define ASPEED_REG_INTERRUPT_CONTROL 0x04 #define ASPEED_REG_VGA_DETECT_CONTROL 0x08 #define ASPEED_REG_CLOCK_CONTROL 0x0C -#define ASPEED_REG_MAX 0xC0 - -#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) -#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) -#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) - -#define ASPEED_ENGINE_ENABLE BIT(0) - -#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 +#define ASPEED_REG_MAX 0xCC + +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 +#define ASPEED_ADC_OP_MODE_STANDBY 1 +#define ASPEED_ADC_OP_MODE_NORMAL 7 +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_ADC_CH7_MODE BIT(12) +#define ASPEED_ADC_CH7_NORMAL 0 +#define ASPEED_ADC_CH7_BAT 1 +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 @@ -226,7 +244,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (model_data->wait_init_sequence) { /* Enable engine in normal mode. */ - writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); /* Wait for initial sequence complete. */ @@ -245,10 +265,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) goto clk_enable_error; - adc_engine_control_reg_val = GENMASK(31, 16) | - ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + adc_engine_control_reg_val = + ASPEED_ADC_CTRL_CHANNEL | + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); + data->base + ASPEED_REG_ENGINE_CONTROL); model_data = of_device_get_match_data(&pdev->dev); indio_dev->name = model_data->model_name; @@ -264,8 +286,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) return 0; iio_register_error: - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: @@ -283,8 +305,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) struct aspeed_adc_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:28 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:28 +0800 Subject: [RESEND v4 00/15] Add support for ast2600 ADC Message-ID: <20210824091243.9393-1-billy_tsai@aspeedtech.com> This patch serials make aspeed_adc.c can support ast2600 and backward compatible. RESEND due to miss some patches when sent patch v4. Change since v3: dt-bindings: - Fix properties:aspeed,int_vref_mv type error. Change since v2: dt-bindings: - Create a new dt-bindings for ast2600 adc aspeed_adc.c: - Splits the patch for more details - Remove version enum and use the flags in model data to distinguish hardware feature - Support trimming data get and set. - Use devm_add_action_or_reset to simplify probe error handling. Changes since v1: dt-bindings: - Fix the aspeed,adc.yaml check error. - Add battery-sensing property. aspeed_adc.c: - Change the init flow: Clock and reference voltage setting should be completed before adc engine enable. - Change the default sampling rate to meet most user case. - Add patch #8 to suppoert battery sensing mode. Billy Tsai (15): dt-bindings: iio: adc: Add ast2600-adc bindings iio: adc: aspeed: completes the bitfield declare. iio: adc: aspeed: set driver data when adc probe. iio: adc: aspeed: Keep model data to driver data. iio: adc: aspeed: Refactory model data structure iio: adc: aspeed: Add vref config function iio: adc: aspeed: Set num_channels with model data iio: adc: aspeed: Use model_data to set clk scaler. iio: adc: aspeed: Use devm_add_action_or_reset. iio: adc: aspeed: Support ast2600 adc. iio: adc: aspeed: Fix the calculate error of clock. iio: adc: aspeed: Add func to set sampling rate. iio: adc: aspeed: Add compensation phase. iio: adc: aspeed: Support battery sensing. iio: adc: aspeed: Get and set trimming data. .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 97 +++ drivers/iio/adc/aspeed_adc.c | 562 +++++++++++++++--- 2 files changed, 569 insertions(+), 90 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:33 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:33 +0800 Subject: [RESEND v4 05/15] iio: adc: aspeed: Refactory model data structure In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-6-billy_tsai@aspeedtech.com> This patch refactory the model data structure to distinguish the function form differnet version of aspeed adc. - Rename the vref_voltag to vref_fixed and add vref driver data When driver probe will check vref_fixed value and store it to vref which isn't const value. - Add num_channels Make num_channles of iio device can be changed by differnet model_data - Add need_prescaler flag and scaler_bit_width The need_prescaler flag used to tell the driver the clock divider needs another prescaler and the scaler_bit_width to set the clock divider bitfiled width. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index d85aa31ee3b1..f03c7921d534 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -63,8 +63,11 @@ struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz unsigned int max_sampling_rate; // Hz - unsigned int vref_voltage; // mV + unsigned int vref_fixed; // mV bool wait_init_sequence; + bool need_prescaler; + u8 scaler_bit_width; + unsigned int num_channels; }; struct aspeed_adc_data { @@ -75,6 +78,7 @@ struct aspeed_adc_data { struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; + int vref; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -118,7 +122,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_voltage; + *val = data->model_data->vref_fixed; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -312,17 +316,23 @@ static int aspeed_adc_remove(struct platform_device *pdev) static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", - .vref_voltage = 2500, // mV + .vref_fixed = 2500, // mV .min_sampling_rate = 10000, .max_sampling_rate = 500000, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct aspeed_adc_model_data ast2500_model_data = { .model_name = "ast2500-adc", - .vref_voltage = 1800, // mV + .vref_fixed = 1800, // mV .min_sampling_rate = 1, .max_sampling_rate = 1000000, .wait_init_sequence = true, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct of_device_id aspeed_adc_matches[] = { -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:31 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:31 +0800 Subject: [RESEND v4 03/15] iio: adc: aspeed: set driver data when adc probe. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-4-billy_tsai@aspeedtech.com> Fix the issue when adc remove will get the null driver data. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 7010d56ac3b9..20462cf659e4 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -201,6 +201,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:32 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:32 +0800 Subject: [RESEND v4 04/15] iio: adc: aspeed: Keep model data to driver data. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-5-billy_tsai@aspeedtech.com> Keep the model data pointer to driver data for reducing the usage of of_device_get_match_data(). Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 20462cf659e4..d85aa31ee3b1 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -69,6 +69,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; + const struct aspeed_adc_model_data *model_data; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -110,8 +111,6 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -119,7 +118,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = model_data->vref_voltage; + *val = data->model_data->vref_voltage; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -138,13 +137,11 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < model_data->min_sampling_rate || - val > model_data->max_sampling_rate) + if (val < data->model_data->min_sampling_rate || + val > data->model_data->max_sampling_rate) return -EINVAL; clk_set_rate(data->clk_scaler->clk, @@ -190,7 +187,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const struct aspeed_adc_model_data *model_data; const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; @@ -201,6 +197,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + data->model_data = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); @@ -241,9 +238,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); - model_data = of_device_get_match_data(&pdev->dev); - - if (model_data->wait_init_sequence) { + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | @@ -273,8 +268,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); - model_data = of_device_get_match_data(&pdev->dev); - indio_dev->name = model_data->model_name; + indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:38 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:38 +0800 Subject: [RESEND v4 10/15] iio: adc: aspeed: Support ast2600 adc. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-11-billy_tsai@aspeedtech.com> Make driver to support ast2600 adc device. - Use shared reset controller - Complete the vref configure function - Add the model data for ast2600 adc Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 106 +++++++++++++++++++++++++++++++++-- 1 file changed, 100 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 1c87e12a0cab..ea3e9a52fcc9 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Aspeed AST2400/2500 ADC + * Aspeed AST2400/2500/2600 ADC * * Copyright (C) 2017 Google, Inc. * Copyright (C) 2021 Aspeed Technology Inc. @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -73,6 +74,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; const struct aspeed_adc_model_data *model_data; + struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -208,14 +210,80 @@ static void aspeed_adc_clk_disable_unprepare(void *data) clk_disable_unprepare(clk); } +static void aspeed_adc_reg_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); + int ret; + u32 adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); if (data->model_data->vref_fixed) { data->vref = data->model_data->vref_fixed; return 0; } + + data->regulator = devm_regulator_get_optional(data->dev, "vref"); + if (!IS_ERR(data->regulator)) { + ret = regulator_enable(data->regulator); + if (ret) + return ret; + ret = devm_add_action_or_reset( + data->dev, aspeed_adc_reg_disable, data->regulator); + if (ret) + return ret; + data->vref = regulator_get_voltage(data->regulator); + /* Conversion from uV to mV */ + data->vref /= 1000; + if ((data->vref >= 1550) && (data->vref <= 2700)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_HIGH), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if ((data->vref >= 900) && (data->vref <= 1650)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_LOW), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Regulator voltage %d not support", + data->vref); + return -EOPNOTSUPP; + } + } else { + if (PTR_ERR(data->regulator) != -ENODEV) + return PTR_ERR(data->regulator); + ret = of_property_read_u32(data->dev->of_node, + "aspeed,int_vref_mv", &data->vref); + if (ret < 0) { + dev_warn(data->dev, + "Using default vref: internal 2500 mv"); + data->vref = 2500; + } + if (data->vref == 2500) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_2500mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if (data->vref == 1200) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_1200mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Voltage %d not support", data->vref); + return -EOPNOTSUPP; + } + } + return 0; } @@ -279,7 +347,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; - data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + data->rst = devm_reset_control_get_shared(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); @@ -297,10 +365,14 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; if (data->model_data->wait_init_sequence) { + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= + FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; /* Enable engine in normal mode. */ - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, - ASPEED_ADC_OP_MODE_NORMAL) | - ASPEED_ADC_ENGINE_ENABLE, + writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); /* Wait for initial sequence complete. */ @@ -326,6 +398,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL | FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | ASPEED_ADC_ENGINE_ENABLE; @@ -376,9 +450,29 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .num_channels = 16, }; +static const struct aspeed_adc_model_data ast2600_adc0_model_data = { + .model_name = "ast2600-adc0", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .scaler_bit_width = 16, + .num_channels = 8, +}; + +static const struct aspeed_adc_model_data ast2600_adc1_model_data = { + .model_name = "ast2600-adc1", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .scaler_bit_width = 16, + .num_channels = 8, +}; + static const struct of_device_id aspeed_adc_matches[] = { { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, + { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, + { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, {}, }; MODULE_DEVICE_TABLE(of, aspeed_adc_matches); @@ -395,5 +489,5 @@ static struct platform_driver aspeed_adc_driver = { module_platform_driver(aspeed_adc_driver); MODULE_AUTHOR("Rick Altherr "); -MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); +MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver"); MODULE_LICENSE("GPL"); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:39 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:39 +0800 Subject: [RESEND v4 11/15] iio: adc: aspeed: Fix the calculate error of clock. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-12-billy_tsai@aspeedtech.com> The adc clcok formula is ast2400/2500: ADC clock period = PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) ast2600: ADC clock period = PCLK * 2 * (ADC0C[15:0] + 1) They all have one fixed divided 2 and the legacy driver didn't handle it. This patch register the fixed factory clock device as the parent of adc clock scaler to fix this issue. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index ea3e9a52fcc9..8fe7da1a651f 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -4,6 +4,12 @@ * * Copyright (C) 2017 Google, Inc. * Copyright (C) 2021 Aspeed Technology Inc. + * + * ADC clock formula: + * Ast2400/Ast2500: + * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) + * Ast2600: + * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1) */ #include @@ -77,6 +83,7 @@ struct aspeed_adc_data { struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; + struct clk_hw *fixed_div_clk; struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; @@ -196,6 +203,13 @@ static void aspeed_adc_unregister_divider(void *data) clk_hw_unregister_divider(clk); } +static void aspeed_adc_unregister_fixed_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_fixed_factor(clk); +} + static void aspeed_adc_reset_assert(void *data) { struct reset_control *rst = data; @@ -312,6 +326,18 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Register ADC clock prescaler with source specified by device tree. */ spin_lock_init(&data->clk_lock); snprintf(clk_parent_name, 32, of_clk_get_parent_name(pdev->dev.of_node, 0)); + snprintf(clk_name, 32, "%s-fixed-div", data->model_data->model_name); + data->fixed_div_clk = clk_hw_register_fixed_factor( + &pdev->dev, clk_name, clk_parent_name, 0, 1, 2); + if (IS_ERR(data->fixed_div_clk)) + return PTR_ERR(data->fixed_div_clk); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_fixed_divider, + data->clk_prescaler); + if (ret) + return ret; + snprintf(clk_parent_name, 32, clk_name); if (data->model_data->need_prescaler) { snprintf(clk_name, 32, "%s-prescaler", data->model_data->model_name); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:34 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:34 +0800 Subject: [RESEND v4 06/15] iio: adc: aspeed: Add vref config function In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-7-billy_tsai@aspeedtech.com> Add the function to check the vref_fixed and set the value to driver data. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index f03c7921d534..f260e40ab9b2 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -122,7 +122,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_fixed; + *val = data->vref; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -187,6 +187,17 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static int aspeed_adc_vref_config(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (data->model_data->vref_fixed) { + data->vref = data->model_data->vref_fixed; + return 0; + } + return 0; +} + static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -242,6 +253,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); + ret = aspeed_adc_vref_config(indio_dev); + if (ret) + goto vref_config_error; + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, @@ -290,6 +305,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: +vref_config_error: reset_control_assert(data->rst); reset_error: clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:29 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:29 +0800 Subject: [RESEND v4 01/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <20210824091243.9393-2-billy_tsai@aspeedtech.com> Add device tree bindings document for the aspeed ast2600 adc device driver. Signed-off-by: Billy Tsai --- .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml new file mode 100644 index 000000000000..248cda7d91e9 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml @@ -0,0 +1,97 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC that forms part of an ASPEED server management processor. + +maintainers: + - Billy Tsai + +description: | + ? 10-bits resolution for 16 voltage channels. + ? The device split into two individual engine and each contains 8 voltage + channels. + ? Channel scanning can be non-continuous. + ? Programmable ADC clock frequency. + ? Programmable upper and lower threshold for each channels. + ? Interrupt when larger or less than threshold for each channels. + ? Support hysteresis for each channels. + ? Built-in a compensating method. + ? Built-in a register to trim internal reference voltage. + ? Internal or External reference voltage. + ? Support 2 Internal reference voltage 1.2v or 2.5v. + ? Integrate dividing circuit for battery sensing. + +properties: + compatible: + enum: + - aspeed,ast2600-adc0 + - aspeed,ast2600-adc1 + + reg: + maxItems: 1 + + clocks: + description: + Input clock used to derive the sample clock. Expected to be the + SoC's APB clock. + + resets: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + vref-supply: + description: + The external regulator supply ADC reference voltage. + + aspeed,int_vref_mv: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [1200, 2500] + description: + ADC internal reference voltage in millivolts. + + aspeed,battery-sensing: + type: boolean + description: + Inform the driver that last channel will be used to sensor battery. + + aspeed,trim-data-valid: + type: boolean + description: | + The ADC reference voltage can be calibrated to obtain the trimming + data which will be stored in otp. This property informs the driver that + the data store in the otp is valid. + +required: + - compatible + - reg + - clocks + - resets + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include + adc0: adc at 1e6e9000 { + compatible = "aspeed,ast2600-adc0"; + reg = <0x1e6e9000 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int_vref_mv = <2500>; + }; + adc1: adc at 1e6e9100 { + compatible = "aspeed,ast2600-adc1"; + reg = <0x1e6e9100 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int_vref_mv = <2500>; + }; +... -- 2.25.1 From robh at kernel.org Tue Aug 24 22:13:04 2021 From: robh at kernel.org (Rob Herring) Date: Tue, 24 Aug 2021 07:13:04 -0500 Subject: [v4 01/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: <20210823070240.12600-2-billy_tsai@aspeedtech.com> References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> <20210823070240.12600-2-billy_tsai@aspeedtech.com> Message-ID: On Mon, Aug 23, 2021 at 03:02:26PM +0800, Billy Tsai wrote: > Add device tree bindings document for the aspeed ast2600 adc device > driver. > > Signed-off-by: Billy Tsai > --- > .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 97 +++++++++++++++++++ > 1 file changed, 97 insertions(+) > create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml > > diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml > new file mode 100644 > index 000000000000..248cda7d91e9 > --- /dev/null > +++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml > @@ -0,0 +1,97 @@ > +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) > +%YAML 1.2 > +--- > +$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml# > +$schema: http://devicetree.org/meta-schemas/core.yaml# > + > +title: ADC that forms part of an ASPEED server management processor. > + > +maintainers: > + - Billy Tsai > + > +description: | > + ? 10-bits resolution for 16 voltage channels. > + ? The device split into two individual engine and each contains 8 voltage > + channels. > + ? Channel scanning can be non-continuous. > + ? Programmable ADC clock frequency. > + ? Programmable upper and lower threshold for each channels. > + ? Interrupt when larger or less than threshold for each channels. > + ? Support hysteresis for each channels. > + ? Built-in a compensating method. > + ? Built-in a register to trim internal reference voltage. > + ? Internal or External reference voltage. > + ? Support 2 Internal reference voltage 1.2v or 2.5v. > + ? Integrate dividing circuit for battery sensing. > + > +properties: > + compatible: > + enum: > + - aspeed,ast2600-adc0 > + - aspeed,ast2600-adc1 What's the difference between 0 and 1? > + > + reg: > + maxItems: 1 > + > + clocks: > + description: > + Input clock used to derive the sample clock. Expected to be the > + SoC's APB clock. How many clocks? > + > + resets: > + maxItems: 1 > + > + "#io-channel-cells": > + const: 1 > + > + vref-supply: > + description: > + The external regulator supply ADC reference voltage. > + > + aspeed,int_vref_mv: Don't use '_' in property names. Use standard unit suffixes as defined in property-units.yaml. > + $ref: /schemas/types.yaml#/definitions/uint32 And then you can drop this. > + enum: [1200, 2500] > + description: > + ADC internal reference voltage in millivolts. > + > + aspeed,battery-sensing: > + type: boolean > + description: > + Inform the driver that last channel will be used to sensor battery. > + > + aspeed,trim-data-valid: > + type: boolean > + description: | > + The ADC reference voltage can be calibrated to obtain the trimming > + data which will be stored in otp. This property informs the driver that > + the data store in the otp is valid. > + > +required: > + - compatible > + - reg > + - clocks > + - resets > + - "#io-channel-cells" > + > +additionalProperties: false > + > +examples: > + - | > + #include > + adc0: adc at 1e6e9000 { > + compatible = "aspeed,ast2600-adc0"; > + reg = <0x1e6e9000 0x100>; > + clocks = <&syscon ASPEED_CLK_APB2>; > + resets = <&syscon ASPEED_RESET_ADC>; > + #io-channel-cells = <1>; > + aspeed,int_vref_mv = <2500>; > + }; > + adc1: adc at 1e6e9100 { > + compatible = "aspeed,ast2600-adc1"; > + reg = <0x1e6e9100 0x100>; > + clocks = <&syscon ASPEED_CLK_APB2>; > + resets = <&syscon ASPEED_RESET_ADC>; > + #io-channel-cells = <1>; > + aspeed,int_vref_mv = <2500>; > + }; > +... > -- > 2.25.1 > > From billy_tsai at aspeedtech.com Wed Aug 25 10:06:47 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Wed, 25 Aug 2021 00:06:47 +0000 Subject: [v4 01/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> <20210823070240.12600-2-billy_tsai@aspeedtech.com> Message-ID: Hi Rob, On 2021/8/24, 8:13 PM, "Rob Herring" wrote: On Mon, Aug 23, 2021 at 03:02:26PM +0800, Billy Tsai wrote: > > +properties: > > + compatible: > > + enum: > > + - aspeed,ast2600-adc0 > > + - aspeed,ast2600-adc1 > What's the difference between 0 and 1? Their trimming data, which is used to calibrate internal reference volage, locates in different address of OTP. Best Regards, Billy Tsai From joel at jms.id.au Wed Aug 25 10:07:55 2021 From: joel at jms.id.au (Joel Stanley) Date: Wed, 25 Aug 2021 00:07:55 +0000 Subject: [PATCH -next v2] soc: aspeed-lpc-ctrl: Fix missing clk_disable_unprepare() on error in aspeed_lpc_ctrl_probe() In-Reply-To: <20210824085522.1849410-1-yangyingliang@huawei.com> References: <20210824085522.1849410-1-yangyingliang@huawei.com> Message-ID: On Tue, 24 Aug 2021 at 08:49, Yang Yingliang wrote: > > Move syscon_regmap_lookup_by_compatible() above the > devm_clk_get() to avoid missing clk_disable_unprepare(). > > Fixes: 2f9b25fa6682 ("soc: aspeed: Re-enable FWH2AHB on AST2600") > Reported-by: Hulk Robot > Signed-off-by: Yang Yingliang Reviewed-by: Joel Stanley Thanks for the patch. I will send it to the soc maintainers for merging. > --- > drivers/soc/aspeed/aspeed-lpc-ctrl.c | 20 ++++++++++---------- > 1 file changed, 10 insertions(+), 10 deletions(-) > > diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c > index 6893c5ec3259..72771e018c42 100644 > --- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c > +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c > @@ -295,6 +295,16 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) > return -ENODEV; > } > > + if (of_device_is_compatible(dev->of_node, "aspeed,ast2600-lpc-ctrl")) { > + lpc_ctrl->fwh2ahb = true; > + > + lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); > + if (IS_ERR(lpc_ctrl->scu)) { > + dev_err(dev, "couldn't find scu\n"); > + return PTR_ERR(lpc_ctrl->scu); > + } > + } > + > lpc_ctrl->clk = devm_clk_get(dev, NULL); > if (IS_ERR(lpc_ctrl->clk)) { > dev_err(dev, "couldn't get clock\n"); > @@ -306,16 +316,6 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) > return rc; > } > > - if (of_device_is_compatible(dev->of_node, "aspeed,ast2600-lpc-ctrl")) { > - lpc_ctrl->fwh2ahb = true; > - > - lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); > - if (IS_ERR(lpc_ctrl->scu)) { > - dev_err(dev, "couldn't find scu\n"); > - return PTR_ERR(lpc_ctrl->scu); > - } > - } > - > lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; > lpc_ctrl->miscdev.name = DEVICE_NAME; > lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; > -- > 2.25.1 > From billy_tsai at aspeedtech.com Tue Aug 24 19:12:40 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:40 +0800 Subject: [RESEND v4 12/15] iio: adc: aspeed: Add func to set sampling rate. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250003.17P03PAo092622@twspam01.aspeedtech.com> Add the function to set the sampling rate and keep the sampling period for a driver used to wait the lastest value. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 58 +++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 8fe7da1a651f..4d979dd7fe88 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -65,6 +65,12 @@ #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 +/* + * When the sampling rate is too high, the ADC may not have enough charging + * time, resulting in a low voltage value. Thus, default use slow sampling + * rate for most user case. + */ +#define ASPEED_ADC_DEF_SAMPLING_RATE 65000 struct aspeed_adc_model_data { const char *model_name; @@ -88,6 +94,7 @@ struct aspeed_adc_data { struct clk_hw *clk_scaler; struct reset_control *rst; int vref; + u32 sample_period_ns; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -119,6 +126,24 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (rate < data->model_data->min_sampling_rate || + rate > data->model_data->max_sampling_rate) + return -EINVAL; + /* Each sampling needs 12 clocks to covert.*/ + clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE); + rate = clk_get_rate(data->clk_scaler->clk); + data->sample_period_ns = DIV_ROUND_UP_ULL( + (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate); + dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate, + data->sample_period_ns); + + return 0; +} + static int aspeed_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -149,17 +174,10 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct aspeed_adc_data *data = iio_priv(indio_dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < data->model_data->min_sampling_rate || - val > data->model_data->max_sampling_rate) - return -EINVAL; - - clk_set_rate(data->clk_scaler->clk, - val * ASPEED_CLOCKS_PER_SAMPLE); - return 0; + return aspeed_adc_set_sampling_rate(indio_dev, val); case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_RAW: @@ -386,6 +404,20 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + ret = clk_prepare_enable(data->clk_scaler->clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; + + ret = aspeed_adc_set_sampling_rate(indio_dev, ASPEED_ADC_DEF_SAMPLING_RATE); + if (ret) + return ret; + ret = aspeed_adc_vref_config(indio_dev); if (ret) return ret; @@ -413,16 +445,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) } /* Start all channels in normal mode. */ - ret = clk_prepare_enable(data->clk_scaler->clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(data->dev, - aspeed_adc_clk_disable_unprepare, - data->clk_scaler->clk); - if (ret) - return ret; - adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); adc_engine_control_reg_val |= -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:43 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:43 +0800 Subject: [RESEND v4 15/15] iio: adc: aspeed: Get and set trimming data. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250003.17P03YZ4092974@twspam01.aspeedtech.com> The adc controller have trimming register for fine-tune the reference voltage. The trimming value come from the otp register which will be written before chip product. This patch will read this otp value and configure it to the adc register when adc controller probe and using dts property "aspeed,trim-data-valid" to determine whether to execute this flow. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 0c5d84e82561..bd7fb23f3510 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -72,6 +74,11 @@ */ #define ASPEED_ADC_DEF_SAMPLING_RATE 65000 +struct aspeed_adc_trim_locate { + const unsigned int offset; + const unsigned int field; +}; + struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz @@ -82,6 +89,7 @@ struct aspeed_adc_model_data { bool bat_sense_sup; u8 scaler_bit_width; unsigned int num_channels; + const struct aspeed_adc_trim_locate *trim_locate; }; struct adc_gain { @@ -136,6 +144,44 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev) +{ + struct device_node *syscon; + struct regmap *scu; + u32 scu_otp, trimming_val; + struct aspeed_adc_data *data = iio_priv(indio_dev); + + syscon = of_find_node_by_name(NULL, "syscon"); + if (syscon == NULL) { + dev_warn(data->dev, "Couldn't find syscon node\n"); + return -EOPNOTSUPP; + } + scu = syscon_node_to_regmap(syscon); + if (IS_ERR(scu)) { + dev_warn(data->dev, "Failed to get syscon regmap\n"); + return -EOPNOTSUPP; + } + if (data->model_data->trim_locate) { + if (regmap_read(scu, data->model_data->trim_locate->offset, + &scu_otp)) { + dev_warn(data->dev, + "Failed to get adc trimming data\n"); + trimming_val = 0x8; + } else { + trimming_val = + ((scu_otp) & + (data->model_data->trim_locate->field)) >> + __ffs(data->model_data->trim_locate->field); + } + dev_dbg(data->dev, + "trimming val = %d, offset = %08x, fields = %08x\n", + trimming_val, data->model_data->trim_locate->offset, + data->model_data->trim_locate->field); + writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM); + } + return 0; +} + static int aspeed_adc_compensation(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -506,6 +552,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid", + NULL)) + aspeed_adc_set_trim_data(indio_dev); + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", NULL)) { if (data->model_data->bat_sense_sup) { @@ -579,6 +629,21 @@ static int aspeed_adc_remove(struct platform_device *pdev) return 0; } +static const struct aspeed_adc_trim_locate ast2500_adc_trim = { + .offset = 0x154, + .field = GENMASK(31, 28), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc0_trim = { + .offset = 0x5d0, + .field = GENMASK(3, 0), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc1_trim = { + .offset = 0x5d0, + .field = GENMASK(7, 4), +}; + static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", .vref_fixed = 2500, // mV @@ -598,6 +663,7 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .need_prescaler = true, .scaler_bit_width = 10, .num_channels = 16, + .trim_locate = &ast2500_adc_trim, }; static const struct aspeed_adc_model_data ast2600_adc0_model_data = { @@ -608,6 +674,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc0_trim, }; static const struct aspeed_adc_model_data ast2600_adc1_model_data = { @@ -618,6 +685,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc1_trim, }; static const struct of_device_id aspeed_adc_matches[] = { -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:37 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:37 +0800 Subject: [RESEND v4 09/15] iio: adc: aspeed: Use devm_add_action_or_reset. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250004.17P04UBG094830@twspam01.aspeedtech.com> This patch use devm_add_action_or_reset to handle the error in probe phase. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 92 +++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 52db38be9699..1c87e12a0cab 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -187,6 +187,27 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_unregister_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_divider(clk); +} + +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -232,6 +253,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) &data->clk_lock); if (IS_ERR(data->clk_prescaler)) return PTR_ERR(data->clk_prescaler); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_divider, + data->clk_prescaler); + if (ret) + return ret; snprintf(clk_parent_name, 32, clk_name); scaler_flags = CLK_SET_RATE_PARENT; } @@ -244,23 +271,30 @@ static int aspeed_adc_probe(struct platform_device *pdev) &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, data->model_data->scaler_bit_width, 0, &data->clk_lock); - if (IS_ERR(data->clk_scaler)) { - ret = PTR_ERR(data->clk_scaler); - goto scaler_error; - } + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); + + ret = devm_add_action_or_reset(data->dev, aspeed_adc_unregister_divider, + data->clk_scaler); + if (ret) + return ret; data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); - ret = PTR_ERR(data->rst); - goto reset_error; + return PTR_ERR(data->rst); } reset_control_deassert(data->rst); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, + data->rst); + if (ret) + return ret; + ret = aspeed_adc_vref_config(indio_dev); if (ret) - goto vref_config_error; + return ret; if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ @@ -277,13 +311,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_INIT_POLLING_TIME, ASPEED_ADC_INIT_TIMEOUT); if (ret) - goto poll_timeout_error; + return ret; } /* Start all channels in normal mode. */ ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) - goto clk_enable_error; + return ret; + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; adc_engine_control_reg_val = ASPEED_ADC_CTRL_CHANNEL | @@ -299,41 +339,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); - if (ret) - goto iio_register_error; - + if (ret) { + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); + return ret; + } return 0; - -iio_register_error: - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); -clk_enable_error: -poll_timeout_error: -vref_config_error: - reset_control_assert(data->rst); -reset_error: - clk_hw_unregister_divider(data->clk_scaler); -scaler_error: - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return ret; } static int aspeed_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct aspeed_adc_data *data = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); - reset_control_assert(data->rst); - clk_hw_unregister_divider(data->clk_scaler); - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return 0; } -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:42 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:42 +0800 Subject: [RESEND v4 14/15] iio: adc: aspeed: Support battery sensing. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250005.17P050Ig095234@twspam01.aspeedtech.com> In ast2600, ADC integrate dividing circuit at last input channel for battery sensing. This patch use the dts property "battery-sensing" to enable this feature makes the last channel of each adc can tolerance higher voltage than reference voltage. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 62 +++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 20caf28dff18..0c5d84e82561 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -79,10 +79,16 @@ struct aspeed_adc_model_data { unsigned int vref_fixed; // mV bool wait_init_sequence; bool need_prescaler; + bool bat_sense_sup; u8 scaler_bit_width; unsigned int num_channels; }; +struct adc_gain { + u8 mult; + u8 div; +}; + struct aspeed_adc_data { struct device *dev; const struct aspeed_adc_model_data *model_data; @@ -96,6 +102,8 @@ struct aspeed_adc_data { int vref; u32 sample_period_ns; int cv; + bool battery_sensing; + struct adc_gain battery_mode_gain; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -103,10 +111,10 @@ struct aspeed_adc_data { .indexed = 1, \ .channel = (_idx), \ .address = (_data_reg_addr), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ } static const struct iio_chan_spec aspeed_adc_iio_channels[] = { @@ -196,14 +204,39 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 adc_engine_control_reg_val; switch (mask) { case IIO_CHAN_INFO_RAW: - *val = readw(data->base + chan->address); + if (data->battery_sensing && chan->channel == 7) { + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_CH7_MODE, + ASPEED_ADC_CH7_BAT) | + ASPEED_ADC_BAT_SENSING_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable battery sensing mode need to wait some time for adc stable + * Experiment result is 1ms. + */ + mdelay(1); + *val = readw(data->base + chan->address); + *val = (*val * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + /* Restore control register value */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + } else + *val = readw(data->base + chan->address); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - *val = data->cv; + if (data->battery_sensing && chan->channel == 7) + *val = (data->cv * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + else + *val = data->cv; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -473,6 +506,23 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", + NULL)) { + if (data->model_data->bat_sense_sup) { + data->battery_sensing = 1; + if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) & + ASPEED_ADC_BAT_SENSING_DIV) { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 1; + } else { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 2; + } + } else + dev_warn(&pdev->dev, + "Failed to enable battey-sensing mode\n"); + } + if (data->model_data->wait_init_sequence) { adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); @@ -555,6 +605,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { .min_sampling_rate = 10000, .max_sampling_rate = 500000, .wait_init_sequence = true, + .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, }; @@ -564,6 +615,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { .min_sampling_rate = 10000, .max_sampling_rate = 500000, .wait_init_sequence = true, + .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, }; -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:36 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:36 +0800 Subject: [RESEND v4 08/15] iio: adc: aspeed: Use model_data to set clk scaler. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250005.17P05x8A096632@twspam01.aspeedtech.com> This patch use need_prescaler and scaler_bit_width to set the adc clock scaler. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 39 +++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 2d6215a91f99..52db38be9699 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -202,9 +202,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; + unsigned long scaler_flags = 0; + char clk_name[32], clk_parent_name[32]; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); if (!indio_dev) @@ -221,24 +222,28 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Register ADC clock prescaler with source specified by device tree. */ spin_lock_init(&data->clk_lock); - clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); - - data->clk_prescaler = clk_hw_register_divider( - &pdev->dev, "prescaler", clk_parent_name, 0, - data->base + ASPEED_REG_CLOCK_CONTROL, - 17, 15, 0, &data->clk_lock); - if (IS_ERR(data->clk_prescaler)) - return PTR_ERR(data->clk_prescaler); - + snprintf(clk_parent_name, 32, of_clk_get_parent_name(pdev->dev.of_node, 0)); + if (data->model_data->need_prescaler) { + snprintf(clk_name, 32, "%s-prescaler", + data->model_data->model_name); + data->clk_prescaler = clk_hw_register_divider( + &pdev->dev, clk_name, clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, + &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + snprintf(clk_parent_name, 32, clk_name); + scaler_flags = CLK_SET_RATE_PARENT; + } /* * Register ADC clock scaler downstream from the prescaler. Allow rate * setting to adjust the prescaler as well. */ + snprintf(clk_name, 32, "%s-scaler", data->model_data->model_name); data->clk_scaler = clk_hw_register_divider( - &pdev->dev, "scaler", "prescaler", - CLK_SET_RATE_PARENT, - data->base + ASPEED_REG_CLOCK_CONTROL, - 0, 10, 0, &data->clk_lock); + &pdev->dev, clk_name, clk_parent_name, scaler_flags, + data->base + ASPEED_REG_CLOCK_CONTROL, 0, + data->model_data->scaler_bit_width, 0, &data->clk_lock); if (IS_ERR(data->clk_scaler)) { ret = PTR_ERR(data->clk_scaler); goto scaler_error; @@ -310,7 +315,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) reset_error: clk_hw_unregister_divider(data->clk_scaler); scaler_error: - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return ret; } @@ -325,7 +331,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return 0; } -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:41 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:41 +0800 Subject: [RESEND v4 13/15] iio: adc: aspeed: Add compensation phase. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250006.17P06O4n097273@twspam01.aspeedtech.com> This patch adds a compensation phase to improve the accurate of ADC measurement. This is the built-in function though input half of the reference voltage to get the ADC offset. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 54 +++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 4d979dd7fe88..20caf28dff18 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -95,6 +95,7 @@ struct aspeed_adc_data { struct reset_control *rst; int vref; u32 sample_period_ns; + int cv; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -104,7 +105,8 @@ struct aspeed_adc_data { .address = (_data_reg_addr), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ } static const struct iio_chan_spec aspeed_adc_iio_channels[] = { @@ -126,6 +128,51 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_compensation(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 index, adc_raw = 0; + u32 adc_engine_control_reg_val; + + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE; + adc_engine_control_reg_val |= + (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE); + /* + * Enable compensating sensing: + * After that, the input voltage of ADC will force to half of the reference + * voltage. So the expected reading raw data will become half of the max + * value. We can get compensating value = 0x200 - ADC read raw value. + * It is recommended to average at least 10 samples to get a final CV. + */ + writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION | + ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable compensating sensing mode need to wait some time for ADC stable + * Experiment result is 1ms. + */ + mdelay(1); + + for (index = 0; index < 16; index++) { + /* + * Waiting for the sampling period ensures that the value acquired + * is fresh each time. + */ + ndelay(data->sample_period_ns); + adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address); + } + adc_raw >>= 4; + data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + dev_dbg(data->dev, "Compensating value = %d\n", data->cv); + + return 0; +} + static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -155,6 +202,10 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, *val = readw(data->base + chan->address); return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = data->cv; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: *val = data->vref; *val2 = ASPEED_RESOLUTION_BITS; @@ -444,6 +495,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; } + aspeed_adc_compensation(indio_dev); /* Start all channels in normal mode. */ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:43 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:43 +0800 Subject: [RESEND v4 15/15] iio: adc: aspeed: Get and set trimming data. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250007.17P0758M097396@twspam01.aspeedtech.com> The adc controller have trimming register for fine-tune the reference voltage. The trimming value come from the otp register which will be written before chip product. This patch will read this otp value and configure it to the adc register when adc controller probe and using dts property "aspeed,trim-data-valid" to determine whether to execute this flow. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 68 ++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 0c5d84e82561..bd7fb23f3510 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -72,6 +74,11 @@ */ #define ASPEED_ADC_DEF_SAMPLING_RATE 65000 +struct aspeed_adc_trim_locate { + const unsigned int offset; + const unsigned int field; +}; + struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz @@ -82,6 +89,7 @@ struct aspeed_adc_model_data { bool bat_sense_sup; u8 scaler_bit_width; unsigned int num_channels; + const struct aspeed_adc_trim_locate *trim_locate; }; struct adc_gain { @@ -136,6 +144,44 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev) +{ + struct device_node *syscon; + struct regmap *scu; + u32 scu_otp, trimming_val; + struct aspeed_adc_data *data = iio_priv(indio_dev); + + syscon = of_find_node_by_name(NULL, "syscon"); + if (syscon == NULL) { + dev_warn(data->dev, "Couldn't find syscon node\n"); + return -EOPNOTSUPP; + } + scu = syscon_node_to_regmap(syscon); + if (IS_ERR(scu)) { + dev_warn(data->dev, "Failed to get syscon regmap\n"); + return -EOPNOTSUPP; + } + if (data->model_data->trim_locate) { + if (regmap_read(scu, data->model_data->trim_locate->offset, + &scu_otp)) { + dev_warn(data->dev, + "Failed to get adc trimming data\n"); + trimming_val = 0x8; + } else { + trimming_val = + ((scu_otp) & + (data->model_data->trim_locate->field)) >> + __ffs(data->model_data->trim_locate->field); + } + dev_dbg(data->dev, + "trimming val = %d, offset = %08x, fields = %08x\n", + trimming_val, data->model_data->trim_locate->offset, + data->model_data->trim_locate->field); + writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM); + } + return 0; +} + static int aspeed_adc_compensation(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -506,6 +552,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid", + NULL)) + aspeed_adc_set_trim_data(indio_dev); + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", NULL)) { if (data->model_data->bat_sense_sup) { @@ -579,6 +629,21 @@ static int aspeed_adc_remove(struct platform_device *pdev) return 0; } +static const struct aspeed_adc_trim_locate ast2500_adc_trim = { + .offset = 0x154, + .field = GENMASK(31, 28), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc0_trim = { + .offset = 0x5d0, + .field = GENMASK(3, 0), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc1_trim = { + .offset = 0x5d0, + .field = GENMASK(7, 4), +}; + static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", .vref_fixed = 2500, // mV @@ -598,6 +663,7 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .need_prescaler = true, .scaler_bit_width = 10, .num_channels = 16, + .trim_locate = &ast2500_adc_trim, }; static const struct aspeed_adc_model_data ast2600_adc0_model_data = { @@ -608,6 +674,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc0_trim, }; static const struct aspeed_adc_model_data ast2600_adc1_model_data = { @@ -618,6 +685,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc1_trim, }; static const struct of_device_id aspeed_adc_matches[] = { -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 24 19:12:37 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 24 Aug 2021 17:12:37 +0800 Subject: [RESEND v4 09/15] iio: adc: aspeed: Use devm_add_action_or_reset. In-Reply-To: <20210824091243.9393-1-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> Message-ID: <202108250007.17P07xB6097496@twspam01.aspeedtech.com> This patch use devm_add_action_or_reset to handle the error in probe phase. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 92 +++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 37 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 52db38be9699..1c87e12a0cab 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -187,6 +187,27 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_unregister_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_divider(clk); +} + +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -232,6 +253,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) &data->clk_lock); if (IS_ERR(data->clk_prescaler)) return PTR_ERR(data->clk_prescaler); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_divider, + data->clk_prescaler); + if (ret) + return ret; snprintf(clk_parent_name, 32, clk_name); scaler_flags = CLK_SET_RATE_PARENT; } @@ -244,23 +271,30 @@ static int aspeed_adc_probe(struct platform_device *pdev) &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, data->model_data->scaler_bit_width, 0, &data->clk_lock); - if (IS_ERR(data->clk_scaler)) { - ret = PTR_ERR(data->clk_scaler); - goto scaler_error; - } + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); + + ret = devm_add_action_or_reset(data->dev, aspeed_adc_unregister_divider, + data->clk_scaler); + if (ret) + return ret; data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); - ret = PTR_ERR(data->rst); - goto reset_error; + return PTR_ERR(data->rst); } reset_control_deassert(data->rst); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, + data->rst); + if (ret) + return ret; + ret = aspeed_adc_vref_config(indio_dev); if (ret) - goto vref_config_error; + return ret; if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ @@ -277,13 +311,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_INIT_POLLING_TIME, ASPEED_ADC_INIT_TIMEOUT); if (ret) - goto poll_timeout_error; + return ret; } /* Start all channels in normal mode. */ ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) - goto clk_enable_error; + return ret; + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; adc_engine_control_reg_val = ASPEED_ADC_CTRL_CHANNEL | @@ -299,41 +339,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); - if (ret) - goto iio_register_error; - + if (ret) { + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); + return ret; + } return 0; - -iio_register_error: - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); -clk_enable_error: -poll_timeout_error: -vref_config_error: - reset_control_assert(data->rst); -reset_error: - clk_hw_unregister_divider(data->clk_scaler); -scaler_error: - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return ret; } static int aspeed_adc_remove(struct platform_device *pdev) { struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct aspeed_adc_data *data = iio_priv(indio_dev); - iio_device_unregister(indio_dev); - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); - reset_control_assert(data->rst); - clk_hw_unregister_divider(data->clk_scaler); - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - return 0; } -- 2.25.1 From yulei.sh at bytedance.com Wed Aug 25 12:14:16 2021 From: yulei.sh at bytedance.com (Lei Yu) Date: Wed, 25 Aug 2021 10:14:16 +0800 Subject: [Phishing Risk] [External] [PATCH v3] media: aspeed-video: ignore interrupts that aren't enabled In-Reply-To: <20210617220229.7352-1-zev@bewilderbeest.net> References: <20210617220229.7352-1-zev@bewilderbeest.net> Message-ID: On Fri, Jun 18, 2021 at 6:02 AM Zev Weiss wrote: > > As partially addressed in commit 65d270acb2d6 ("media: aspeed: clear > garbage interrupts"), the ASpeed video engine sometimes asserts > interrupts that the driver hasn't enabled. In addition to the > CAPTURE_COMPLETE and FRAME_COMPLETE interrupts dealt with in that > patch, COMP_READY has also been observed. Instead of playing > whack-a-mole with each one individually, we can instead just blanket > ignore everything we haven't explicitly enabled. > > Signed-off-by: Zev Weiss > --- > > Changes since v2 [1]: > - minor commit message improvements > > Changes since v1 [0]: > - dropped error message > - switched to a blanket-ignore approach as suggested by Ryan > > [0] https://lore.kernel.org/linux-arm-kernel/20201215024542.18888-1-zev at bewilderbeest.net/ > [1] https://lore.kernel.org/openbmc/20210506234048.3214-1-zev at bewilderbeest.net/ > > drivers/media/platform/aspeed-video.c | 16 ++++++---------- > 1 file changed, 6 insertions(+), 10 deletions(-) > > diff --git a/drivers/media/platform/aspeed-video.c b/drivers/media/platform/aspeed-video.c > index 7bb6babdcade..77611c296a25 100644 > --- a/drivers/media/platform/aspeed-video.c > +++ b/drivers/media/platform/aspeed-video.c > @@ -563,6 +563,12 @@ static irqreturn_t aspeed_video_irq(int irq, void *arg) > struct aspeed_video *video = arg; > u32 sts = aspeed_video_read(video, VE_INTERRUPT_STATUS); > > + /* > + * Hardware sometimes asserts interrupts that we haven't actually > + * enabled; ignore them if so. > + */ > + sts &= aspeed_video_read(video, VE_INTERRUPT_CTRL); > + Without the patch, the driver gets an unhandled IRQ in a few hours after the KVM page is opened. This patch is tested on Bytedance's OpenBMC system and the issue has not been reproduced anymore. Tested-by: Lei YU -- BRs, Lei YU From dan.j.williams at intel.com Thu Aug 26 08:58:53 2021 From: dan.j.williams at intel.com (Dan Williams) Date: Wed, 25 Aug 2021 15:58:53 -0700 Subject: [PATCH v2 06/15] peci: Add core infrastructure In-Reply-To: <20210803113134.2262882-7-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-7-iwona.winiarska@intel.com> Message-ID: On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska wrote: > > Intel processors provide access for various services designed to support > processor and DRAM thermal management, platform manageability and > processor interface tuning and diagnostics. > Those services are available via the Platform Environment Control > Interface (PECI) that provides a communication channel between the > processor and the Baseboard Management Controller (BMC) or other > platform management device. > > This change introduces PECI subsystem by adding the initial core module > and API for controller drivers. > > Co-developed-by: Jason M Bills > Signed-off-by: Jason M Bills > Co-developed-by: Jae Hyun Yoo > Signed-off-by: Jae Hyun Yoo > Signed-off-by: Iwona Winiarska > Reviewed-by: Pierre-Louis Bossart > --- > MAINTAINERS | 9 +++ > drivers/Kconfig | 3 + > drivers/Makefile | 1 + > drivers/peci/Kconfig | 15 ++++ > drivers/peci/Makefile | 5 ++ > drivers/peci/core.c | 155 ++++++++++++++++++++++++++++++++++++++++ > drivers/peci/internal.h | 16 +++++ > include/linux/peci.h | 99 +++++++++++++++++++++++++ > 8 files changed, 303 insertions(+) > create mode 100644 drivers/peci/Kconfig > create mode 100644 drivers/peci/Makefile > create mode 100644 drivers/peci/core.c > create mode 100644 drivers/peci/internal.h > create mode 100644 include/linux/peci.h > > diff --git a/MAINTAINERS b/MAINTAINERS > index 7cdab7229651..d411974aaa5e 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -14503,6 +14503,15 @@ L: platform-driver-x86 at vger.kernel.org > S: Maintained > F: drivers/platform/x86/peaq-wmi.c > > +PECI SUBSYSTEM > +M: Iwona Winiarska > +R: Jae Hyun Yoo > +L: openbmc at lists.ozlabs.org (moderated for non-subscribers) > +S: Supported > +F: Documentation/devicetree/bindings/peci/ > +F: drivers/peci/ > +F: include/linux/peci.h > + > PENSANDO ETHERNET DRIVERS > M: Shannon Nelson > M: drivers at pensando.io > diff --git a/drivers/Kconfig b/drivers/Kconfig > index 8bad63417a50..f472b3d972b3 100644 > --- a/drivers/Kconfig > +++ b/drivers/Kconfig > @@ -236,4 +236,7 @@ source "drivers/interconnect/Kconfig" > source "drivers/counter/Kconfig" > > source "drivers/most/Kconfig" > + > +source "drivers/peci/Kconfig" > + > endmenu > diff --git a/drivers/Makefile b/drivers/Makefile > index 27c018bdf4de..8d96f0c3dde5 100644 > --- a/drivers/Makefile > +++ b/drivers/Makefile > @@ -189,3 +189,4 @@ obj-$(CONFIG_GNSS) += gnss/ > obj-$(CONFIG_INTERCONNECT) += interconnect/ > obj-$(CONFIG_COUNTER) += counter/ > obj-$(CONFIG_MOST) += most/ > +obj-$(CONFIG_PECI) += peci/ > diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig > new file mode 100644 > index 000000000000..71a4ad81225a > --- /dev/null > +++ b/drivers/peci/Kconfig > @@ -0,0 +1,15 @@ > +# SPDX-License-Identifier: GPL-2.0-only > + > +menuconfig PECI > + tristate "PECI support" > + help > + The Platform Environment Control Interface (PECI) is an interface > + that provides a communication channel to Intel processors and > + chipset components from external monitoring or control devices. > + > + If you are building a Baseboard Management Controller (BMC) kernel > + for Intel platform say Y here and also to the specific driver for > + your adapter(s) below. If unsure say N. > + > + This support is also available as a module. If so, the module > + will be called peci. > diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile > new file mode 100644 > index 000000000000..e789a354e842 > --- /dev/null > +++ b/drivers/peci/Makefile > @@ -0,0 +1,5 @@ > +# SPDX-License-Identifier: GPL-2.0-only > + > +# Core functionality > +peci-y := core.o > +obj-$(CONFIG_PECI) += peci.o > diff --git a/drivers/peci/core.c b/drivers/peci/core.c > new file mode 100644 > index 000000000000..7b3938af0396 > --- /dev/null > +++ b/drivers/peci/core.c > @@ -0,0 +1,155 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (c) 2018-2021 Intel Corporation > + > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt This looks like overkill for only one print statement in this module, especially when the dev_ print helpers offer more detail. > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include "internal.h" > + > +static DEFINE_IDA(peci_controller_ida); > + > +static void peci_controller_dev_release(struct device *dev) > +{ > + struct peci_controller *controller = to_peci_controller(dev); > + > + pm_runtime_disable(&controller->dev); This seems late to be disabling power management, the device is about to be freed. Keep in mind the lifetime of the this object can be artificially prolonged. I expect this to be done when the device is unregistered from the bus. > + > + mutex_destroy(&controller->bus_lock); > + ida_free(&peci_controller_ida, controller->id); > + fwnode_handle_put(controller->dev.fwnode); Shouldn't the get / put of this handle reference be bound to specific accesses not held for the entire lifetime of the object? At a minimum it seems to be a reference that can taken at registration and dropped at unregistration. > + kfree(controller); > +} > + > +struct device_type peci_controller_type = { > + .release = peci_controller_dev_release, > +}; > + > +static struct peci_controller *peci_controller_alloc(struct device *dev, > + struct peci_controller_ops *ops) > +{ > + struct fwnode_handle *node = fwnode_handle_get(dev_fwnode(dev)); > + struct peci_controller *controller; > + int ret; > + > + if (!ops->xfer) > + return ERR_PTR(-EINVAL); > + > + controller = kzalloc(sizeof(*controller), GFP_KERNEL); > + if (!controller) > + return ERR_PTR(-ENOMEM); > + > + ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL); > + if (ret < 0) > + goto err; > + controller->id = ret; > + > + controller->ops = ops; > + > + controller->dev.parent = dev; > + controller->dev.bus = &peci_bus_type; > + controller->dev.type = &peci_controller_type; > + controller->dev.fwnode = node; > + controller->dev.of_node = to_of_node(node); > + > + device_initialize(&controller->dev); > + > + mutex_init(&controller->bus_lock); > + > + pm_runtime_no_callbacks(&controller->dev); > + pm_suspend_ignore_children(&controller->dev, true); > + pm_runtime_enable(&controller->dev); Per above, are you sure unregistered devices need pm_runtime enabled? Rest looks ok to me. From dan.j.williams at intel.com Thu Aug 26 11:35:53 2021 From: dan.j.williams at intel.com (Dan Williams) Date: Wed, 25 Aug 2021 18:35:53 -0700 Subject: [PATCH v2 07/15] peci: Add peci-aspeed controller driver In-Reply-To: <20210803113134.2262882-8-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-8-iwona.winiarska@intel.com> Message-ID: On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska wrote: > > From: Jae Hyun Yoo > > ASPEED AST24xx/AST25xx/AST26xx SoCs supports the PECI electrical > interface (a.k.a PECI wire). Maybe a one sentence blurb here and in the Kconfig reminding people why they should care if they have a PECI driver or not? > > Signed-off-by: Jae Hyun Yoo > Co-developed-by: Iwona Winiarska > Signed-off-by: Iwona Winiarska > Reviewed-by: Pierre-Louis Bossart > --- > MAINTAINERS | 9 + > drivers/peci/Kconfig | 6 + > drivers/peci/Makefile | 3 + > drivers/peci/controller/Kconfig | 16 + > drivers/peci/controller/Makefile | 3 + > drivers/peci/controller/peci-aspeed.c | 445 ++++++++++++++++++++++++++ > 6 files changed, 482 insertions(+) > create mode 100644 drivers/peci/controller/Kconfig > create mode 100644 drivers/peci/controller/Makefile > create mode 100644 drivers/peci/controller/peci-aspeed.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index d411974aaa5e..6e9d53ff68ab 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -2866,6 +2866,15 @@ S: Maintained > F: Documentation/hwmon/asc7621.rst > F: drivers/hwmon/asc7621.c > > +ASPEED PECI CONTROLLER > +M: Iwona Winiarska > +M: Jae Hyun Yoo > +L: linux-aspeed at lists.ozlabs.org (moderated for non-subscribers) > +L: openbmc at lists.ozlabs.org (moderated for non-subscribers) > +S: Supported > +F: Documentation/devicetree/bindings/peci/peci-aspeed.yaml > +F: drivers/peci/controller/peci-aspeed.c > + > ASPEED PINCTRL DRIVERS > M: Andrew Jeffery > L: linux-aspeed at lists.ozlabs.org (moderated for non-subscribers) > diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig > index 71a4ad81225a..99279df97a78 100644 > --- a/drivers/peci/Kconfig > +++ b/drivers/peci/Kconfig > @@ -13,3 +13,9 @@ menuconfig PECI > > This support is also available as a module. If so, the module > will be called peci. > + > +if PECI > + > +source "drivers/peci/controller/Kconfig" > + > +endif # PECI > diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile > index e789a354e842..926d8df15cbd 100644 > --- a/drivers/peci/Makefile > +++ b/drivers/peci/Makefile > @@ -3,3 +3,6 @@ > # Core functionality > peci-y := core.o > obj-$(CONFIG_PECI) += peci.o > + > +# Hardware specific bus drivers > +obj-y += controller/ > diff --git a/drivers/peci/controller/Kconfig b/drivers/peci/controller/Kconfig > new file mode 100644 > index 000000000000..6d48df08db1c > --- /dev/null > +++ b/drivers/peci/controller/Kconfig > @@ -0,0 +1,16 @@ > +# SPDX-License-Identifier: GPL-2.0-only > + > +config PECI_ASPEED > + tristate "ASPEED PECI support" > + depends on ARCH_ASPEED || COMPILE_TEST > + depends on OF > + depends on HAS_IOMEM > + help > + This option enables PECI controller driver for ASPEED AST2400, > + AST2500 and AST2600 SoCs. > + > + Say Y here if your system runs on ASPEED SoC and you are using it > + as BMC for Intel platform. > + > + This driver can also be built as a module. If so, the module will > + be called peci-aspeed. > diff --git a/drivers/peci/controller/Makefile b/drivers/peci/controller/Makefile > new file mode 100644 > index 000000000000..022c28ef1bf0 > --- /dev/null > +++ b/drivers/peci/controller/Makefile > @@ -0,0 +1,3 @@ > +# SPDX-License-Identifier: GPL-2.0-only > + > +obj-$(CONFIG_PECI_ASPEED) += peci-aspeed.o > diff --git a/drivers/peci/controller/peci-aspeed.c b/drivers/peci/controller/peci-aspeed.c > new file mode 100644 > index 000000000000..1d708c983749 > --- /dev/null > +++ b/drivers/peci/controller/peci-aspeed.c > @@ -0,0 +1,445 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (C) 2012-2017 ASPEED Technology Inc. > +// Copyright (c) 2018-2021 Intel Corporation Why different copyright capitalization? > + > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > +#include > + > +#include Why is this included? > + > +/* ASPEED PECI Registers */ > +/* Control Register */ > +#define ASPEED_PECI_CTRL 0x00 > +#define ASPEED_PECI_CTRL_SAMPLING_MASK GENMASK(19, 16) > +#define ASPEED_PECI_CTRL_RD_MODE_MASK GENMASK(13, 12) > +#define ASPEED_PECI_CTRL_RD_MODE_DBG BIT(13) > +#define ASPEED_PECI_CTRL_RD_MODE_COUNT BIT(12) > +#define ASPEED_PECI_CTRL_CLK_SOURCE BIT(11) > +#define ASPEED_PECI_CTRL_CLK_DIV_MASK GENMASK(10, 8) > +#define ASPEED_PECI_CTRL_INVERT_OUT BIT(7) > +#define ASPEED_PECI_CTRL_INVERT_IN BIT(6) > +#define ASPEED_PECI_CTRL_BUS_CONTENTION_EN BIT(5) > +#define ASPEED_PECI_CTRL_PECI_EN BIT(4) > +#define ASPEED_PECI_CTRL_PECI_CLK_EN BIT(0) > + > +/* Timing Negotiation Register */ > +#define ASPEED_PECI_TIMING_NEGOTIATION 0x04 > +#define ASPEED_PECI_T_NEGO_MSG_MASK GENMASK(15, 8) > +#define ASPEED_PECI_T_NEGO_ADDR_MASK GENMASK(7, 0) > + > +/* Command Register */ > +#define ASPEED_PECI_CMD 0x08 > +#define ASPEED_PECI_CMD_PIN_MONITORING BIT(31) > +#define ASPEED_PECI_CMD_STS_MASK GENMASK(27, 24) > +#define ASPEED_PECI_CMD_STS_ADDR_T_NEGO 0x3 > +#define ASPEED_PECI_CMD_IDLE_MASK \ > + (ASPEED_PECI_CMD_STS_MASK | ASPEED_PECI_CMD_PIN_MONITORING) > +#define ASPEED_PECI_CMD_FIRE BIT(0) > + > +/* Read/Write Length Register */ > +#define ASPEED_PECI_RW_LENGTH 0x0c > +#define ASPEED_PECI_AW_FCS_EN BIT(31) > +#define ASPEED_PECI_RD_LEN_MASK GENMASK(23, 16) > +#define ASPEED_PECI_WR_LEN_MASK GENMASK(15, 8) > +#define ASPEED_PECI_TARGET_ADDR_MASK GENMASK(7, 0) > + > +/* Expected FCS Data Register */ > +#define ASPEED_PECI_EXPECTED_FCS 0x10 > +#define ASPEED_PECI_EXPECTED_RD_FCS_MASK GENMASK(23, 16) > +#define ASPEED_PECI_EXPECTED_AW_FCS_AUTO_MASK GENMASK(15, 8) > +#define ASPEED_PECI_EXPECTED_WR_FCS_MASK GENMASK(7, 0) > + > +/* Captured FCS Data Register */ > +#define ASPEED_PECI_CAPTURED_FCS 0x14 > +#define ASPEED_PECI_CAPTURED_RD_FCS_MASK GENMASK(23, 16) > +#define ASPEED_PECI_CAPTURED_WR_FCS_MASK GENMASK(7, 0) > + > +/* Interrupt Register */ > +#define ASPEED_PECI_INT_CTRL 0x18 > +#define ASPEED_PECI_TIMING_NEGO_SEL_MASK GENMASK(31, 30) > +#define ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO 0 > +#define ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO 1 > +#define ASPEED_PECI_MESSAGE_NEGO 2 > +#define ASPEED_PECI_INT_MASK GENMASK(4, 0) > +#define ASPEED_PECI_INT_BUS_TIMEOUT BIT(4) > +#define ASPEED_PECI_INT_BUS_CONTENTION BIT(3) > +#define ASPEED_PECI_INT_WR_FCS_BAD BIT(2) > +#define ASPEED_PECI_INT_WR_FCS_ABORT BIT(1) > +#define ASPEED_PECI_INT_CMD_DONE BIT(0) > + > +/* Interrupt Status Register */ > +#define ASPEED_PECI_INT_STS 0x1c > +#define ASPEED_PECI_INT_TIMING_RESULT_MASK GENMASK(29, 16) > + /* bits[4..0]: Same bit fields in the 'Interrupt Register' */ > + > +/* Rx/Tx Data Buffer Registers */ > +#define ASPEED_PECI_WR_DATA0 0x20 > +#define ASPEED_PECI_WR_DATA1 0x24 > +#define ASPEED_PECI_WR_DATA2 0x28 > +#define ASPEED_PECI_WR_DATA3 0x2c > +#define ASPEED_PECI_RD_DATA0 0x30 > +#define ASPEED_PECI_RD_DATA1 0x34 > +#define ASPEED_PECI_RD_DATA2 0x38 > +#define ASPEED_PECI_RD_DATA3 0x3c > +#define ASPEED_PECI_WR_DATA4 0x40 > +#define ASPEED_PECI_WR_DATA5 0x44 > +#define ASPEED_PECI_WR_DATA6 0x48 > +#define ASPEED_PECI_WR_DATA7 0x4c > +#define ASPEED_PECI_RD_DATA4 0x50 > +#define ASPEED_PECI_RD_DATA5 0x54 > +#define ASPEED_PECI_RD_DATA6 0x58 > +#define ASPEED_PECI_RD_DATA7 0x5c > +#define ASPEED_PECI_DATA_BUF_SIZE_MAX 32 > + > +/* Timing Negotiation */ > +#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT 8 > +#define ASPEED_PECI_RD_SAMPLING_POINT_MAX (BIT(4) - 1) > +#define ASPEED_PECI_CLK_DIV_DEFAULT 0 > +#define ASPEED_PECI_CLK_DIV_MAX (BIT(3) - 1) > +#define ASPEED_PECI_MSG_TIMING_DEFAULT 1 > +#define ASPEED_PECI_MSG_TIMING_MAX (BIT(8) - 1) > +#define ASPEED_PECI_ADDR_TIMING_DEFAULT 1 > +#define ASPEED_PECI_ADDR_TIMING_MAX (BIT(8) - 1) > + > +/* Timeout */ > +#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_US (50 * USEC_PER_MSEC) > +#define ASPEED_PECI_IDLE_CHECK_INTERVAL_US (10 * USEC_PER_MSEC) > +#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT (1000) > +#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX (1000) > + > +struct aspeed_peci { > + struct peci_controller *controller; > + struct device *dev; > + void __iomem *base; > + struct clk *clk; > + struct reset_control *rst; > + int irq; > + spinlock_t lock; /* to sync completion status handling */ > + struct completion xfer_complete; > + u32 status; > + u32 cmd_timeout_ms; > + u32 msg_timing; > + u32 addr_timing; > + u32 rd_sampling_point; > + u32 clk_div; > +}; > + > +static void aspeed_peci_init_regs(struct aspeed_peci *priv) > +{ > + u32 val; > + > + val = FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, ASPEED_PECI_CLK_DIV_DEFAULT); > + val |= ASPEED_PECI_CTRL_PECI_CLK_EN; > + writel(val, priv->base + ASPEED_PECI_CTRL); > + /* > + * Timing negotiation period setting. > + * The unit of the programmed value is 4 times of PECI clock period. > + */ > + val = FIELD_PREP(ASPEED_PECI_T_NEGO_MSG_MASK, priv->msg_timing); > + val |= FIELD_PREP(ASPEED_PECI_T_NEGO_ADDR_MASK, priv->addr_timing); > + writel(val, priv->base + ASPEED_PECI_TIMING_NEGOTIATION); > + > + /* Clear interrupts */ > + val = readl(priv->base + ASPEED_PECI_INT_STS) | ASPEED_PECI_INT_MASK; > + writel(val, priv->base + ASPEED_PECI_INT_STS); > + > + /* Set timing negotiation mode and enable interrupts */ > + val = FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO); > + val |= ASPEED_PECI_INT_MASK; > + writel(val, priv->base + ASPEED_PECI_INT_CTRL); > + > + val = FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, priv->rd_sampling_point); > + val |= FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, priv->clk_div); > + val |= ASPEED_PECI_CTRL_PECI_EN; > + val |= ASPEED_PECI_CTRL_PECI_CLK_EN; > + writel(val, priv->base + ASPEED_PECI_CTRL); > +} > + > +static inline int aspeed_peci_check_idle(struct aspeed_peci *priv) > +{ > + u32 cmd_sts = readl(priv->base + ASPEED_PECI_CMD); > + > + if (FIELD_GET(ASPEED_PECI_CMD_STS_MASK, cmd_sts) == ASPEED_PECI_CMD_STS_ADDR_T_NEGO) > + aspeed_peci_init_regs(priv); > + > + return readl_poll_timeout(priv->base + ASPEED_PECI_CMD, > + cmd_sts, > + !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK), > + ASPEED_PECI_IDLE_CHECK_INTERVAL_US, > + ASPEED_PECI_IDLE_CHECK_TIMEOUT_US); > +} > + > +static int aspeed_peci_xfer(struct peci_controller *controller, > + u8 addr, struct peci_request *req) > +{ > + struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent); > + unsigned long flags, timeout = msecs_to_jiffies(priv->cmd_timeout_ms); > + u32 peci_head; > + int ret; > + > + if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX || > + req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX) > + return -EINVAL; > + > + /* Check command sts and bus idle state */ > + ret = aspeed_peci_check_idle(priv); > + if (ret) > + return ret; /* -ETIMEDOUT */ > + > + spin_lock_irqsave(&priv->lock, flags); > + reinit_completion(&priv->xfer_complete); > + > + peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) | > + FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) | > + FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len); > + > + writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); > + > + memcpy_toio(priv->base + ASPEED_PECI_WR_DATA0, req->tx.buf, min_t(u8, req->tx.len, 16)); > + if (req->tx.len > 16) > + memcpy_toio(priv->base + ASPEED_PECI_WR_DATA4, req->tx.buf + 16, > + req->tx.len - 16); > + > + dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); > + print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req->tx.len); On CONFIG_DYNAMIC_DEBUG=n builds the kernel will do all the work of reading through this buffer, but skip emitting it. Are you sure you want to pay that overhead for every transaction? > + > + priv->status = 0; > + writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); > + spin_unlock_irqrestore(&priv->lock, flags); > + > + ret = wait_for_completion_interruptible_timeout(&priv->xfer_complete, timeout); spin_lock_irqsave() says "I don't know if interrupts are disabled already, so I'll save the state, whatever it is, and restore later" wait_for_completion_interruptible_timeout() says "I know I am in a sleepable context where interrupts are enabled" So, one of those is wrong, i.e. should it be spin_{lock,unlock}_irq()? > + if (ret < 0) > + return ret; > + > + if (ret == 0) { > + dev_dbg(priv->dev, "Timeout waiting for a response!\n"); > + return -ETIMEDOUT; > + } > + > + spin_lock_irqsave(&priv->lock, flags); > + > + writel(0, priv->base + ASPEED_PECI_CMD); > + > + if (priv->status != ASPEED_PECI_INT_CMD_DONE) { > + spin_unlock_irqrestore(&priv->lock, flags); > + dev_dbg(priv->dev, "No valid response!\n"); > + return -EIO; > + } > + > + spin_unlock_irqrestore(&priv->lock, flags); > + > + memcpy_fromio(req->rx.buf, priv->base + ASPEED_PECI_RD_DATA0, min_t(u8, req->rx.len, 16)); > + if (req->rx.len > 16) > + memcpy_fromio(req->rx.buf + 16, priv->base + ASPEED_PECI_RD_DATA4, > + req->rx.len - 16); > + > + print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req->rx.len); > + > + return 0; > +} > + > +static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) > +{ > + struct aspeed_peci *priv = arg; > + u32 status; > + > + spin_lock(&priv->lock); > + status = readl(priv->base + ASPEED_PECI_INT_STS); > + writel(status, priv->base + ASPEED_PECI_INT_STS); > + priv->status |= (status & ASPEED_PECI_INT_MASK); > + > + /* > + * In most cases, interrupt bits will be set one by one but also note > + * that multiple interrupt bits could be set at the same time. > + */ > + if (status & ASPEED_PECI_INT_BUS_TIMEOUT) > + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_BUS_TIMEOUT\n"); > + > + if (status & ASPEED_PECI_INT_BUS_CONTENTION) > + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_BUS_CONTENTION\n"); > + > + if (status & ASPEED_PECI_INT_WR_FCS_BAD) > + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_WR_FCS_BAD\n"); > + > + if (status & ASPEED_PECI_INT_WR_FCS_ABORT) > + dev_dbg_ratelimited(priv->dev, "ASPEED_PECI_INT_WR_FCS_ABORT\n"); Are you sure these would not be better as tracepoints? If you're debugging an interrupt related failure, the ratelimiting might get in your way when you really need to know when one of these error interrupts fire relative to another event. > + > + /* > + * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE bit > + * set even in an error case. > + */ > + if (status & ASPEED_PECI_INT_CMD_DONE) > + complete(&priv->xfer_complete); Hmm, no need to check if there was a sequencing error, like a command was never submitted? > + > + spin_unlock(&priv->lock); > + > + return IRQ_HANDLED; > +} > + > +static void aspeed_peci_property_sanitize(struct device *dev, const char *propname, > + u32 min, u32 max, u32 default_val, u32 *propval) > +{ > + u32 val; > + int ret; > + > + ret = device_property_read_u32(dev, propname, &val); > + if (ret) { > + val = default_val; > + } else if (val > max || val < min) { > + dev_warn(dev, "Invalid %s: %u, falling back to: %u\n", > + propname, val, default_val); > + > + val = default_val; > + } > + > + *propval = val; > +} > + > +static void aspeed_peci_property_setup(struct aspeed_peci *priv) > +{ > + aspeed_peci_property_sanitize(priv->dev, "aspeed,clock-divider", > + 0, ASPEED_PECI_CLK_DIV_MAX, > + ASPEED_PECI_CLK_DIV_DEFAULT, &priv->clk_div); > + aspeed_peci_property_sanitize(priv->dev, "aspeed,msg-timing", > + 0, ASPEED_PECI_MSG_TIMING_MAX, > + ASPEED_PECI_MSG_TIMING_DEFAULT, &priv->msg_timing); > + aspeed_peci_property_sanitize(priv->dev, "aspeed,addr-timing", > + 0, ASPEED_PECI_ADDR_TIMING_MAX, > + ASPEED_PECI_ADDR_TIMING_DEFAULT, &priv->addr_timing); > + aspeed_peci_property_sanitize(priv->dev, "aspeed,rd-sampling-point", > + 0, ASPEED_PECI_RD_SAMPLING_POINT_MAX, > + ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT, > + &priv->rd_sampling_point); > + aspeed_peci_property_sanitize(priv->dev, "cmd-timeout-ms", > + 1, ASPEED_PECI_CMD_TIMEOUT_MS_MAX, > + ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT, &priv->cmd_timeout_ms); > +} > + > +static struct peci_controller_ops aspeed_ops = { > + .xfer = aspeed_peci_xfer, > +}; > + > +static void aspeed_peci_reset_control_release(void *data) > +{ > + reset_control_assert(data); > +} > + > +int aspeed_peci_reset_control_deassert(struct device *dev, struct reset_control *rst) I'd recommend naming this devm_aspeed_peci_reset_control_deassert(), because I came looking here from reading probe for why there was no reassertion of reset on driver ->remove(). > +{ > + int ret; > + > + ret = reset_control_deassert(rst); > + if (ret) > + return ret; > + > + return devm_add_action_or_reset(dev, aspeed_peci_reset_control_release, rst); > +} > + > +static void aspeed_peci_clk_release(void *data) > +{ > + clk_disable_unprepare(data); > +} > + > +static int aspeed_peci_clk_enable(struct device *dev, struct clk *clk) ...ditto on the devm prefix, just to speed readability. > +{ > + int ret; > + > + ret = clk_prepare_enable(clk); > + if (ret) > + return ret; > + > + return devm_add_action_or_reset(dev, aspeed_peci_clk_release, clk); > +} > + > +static int aspeed_peci_probe(struct platform_device *pdev) > +{ > + struct peci_controller *controller; > + struct aspeed_peci *priv; > + int ret; > + > + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > + if (!priv) > + return -ENOMEM; > + > + priv->dev = &pdev->dev; > + dev_set_drvdata(priv->dev, priv); > + > + priv->base = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(priv->base)) > + return PTR_ERR(priv->base); > + > + priv->irq = platform_get_irq(pdev, 0); > + if (!priv->irq) > + return priv->irq; > + > + ret = devm_request_irq(&pdev->dev, priv->irq, aspeed_peci_irq_handler, > + 0, "peci-aspeed", priv); > + if (ret) > + return ret; > + > + init_completion(&priv->xfer_complete); > + spin_lock_init(&priv->lock); > + > + priv->rst = devm_reset_control_get(&pdev->dev, NULL); > + if (IS_ERR(priv->rst)) > + return dev_err_probe(priv->dev, PTR_ERR(priv->rst), > + "failed to get reset control\n"); > + > + ret = aspeed_peci_reset_control_deassert(priv->dev, priv->rst); > + if (ret) > + return dev_err_probe(priv->dev, ret, "cannot deassert reset control\n"); > + > + priv->clk = devm_clk_get(priv->dev, NULL); > + if (IS_ERR(priv->clk)) > + return dev_err_probe(priv->dev, PTR_ERR(priv->clk), "failed to get clk\n"); > + > + ret = aspeed_peci_clk_enable(priv->dev, priv->clk); > + if (ret) > + return dev_err_probe(priv->dev, ret, "failed to enable clock\n"); > + > + aspeed_peci_property_setup(priv); > + > + aspeed_peci_init_regs(priv); > + > + controller = devm_peci_controller_add(priv->dev, &aspeed_ops); > + if (IS_ERR(controller)) > + return dev_err_probe(priv->dev, PTR_ERR(controller), > + "failed to add aspeed peci controller\n"); > + > + priv->controller = controller; > + > + return 0; > +} > + > +static const struct of_device_id aspeed_peci_of_table[] = { > + { .compatible = "aspeed,ast2400-peci", }, > + { .compatible = "aspeed,ast2500-peci", }, > + { .compatible = "aspeed,ast2600-peci", }, > + { } > +}; > +MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); > + > +static struct platform_driver aspeed_peci_driver = { > + .probe = aspeed_peci_probe, > + .driver = { > + .name = "peci-aspeed", > + .of_match_table = aspeed_peci_of_table, > + }, > +}; > +module_platform_driver(aspeed_peci_driver); > + > +MODULE_AUTHOR("Ryan Chen "); > +MODULE_AUTHOR("Jae Hyun Yoo "); > +MODULE_DESCRIPTION("ASPEED PECI driver"); > +MODULE_LICENSE("GPL"); > +MODULE_IMPORT_NS(PECI); > -- > 2.31.1 > From chiawei_wang at aspeedtech.com Thu Aug 26 16:16:19 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 26 Aug 2021 14:16:19 +0800 Subject: [PATCH v3 0/4] arm: aspeed: Add eSPI support Message-ID: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> This patch series add the driver support for the eSPI controller of Aspeed 5/6th generation SoCs. This controller is a slave device communicating with a master over Enhanced Serial Peripheral Interface (eSPI). It supports all of the 4 eSPI channels, namely peripheral, virtual wire, out-of-band, and flash, and operates at max frequency of 66MHz. v3: - remove the redundant patch "clk: aspeed: Add eSPI reset bit" - fix missing header inclusion reported by test bot - fix dt-bindings error reported by yamllint v2: - remove irqchip implementation - merge per-channel drivers into single one to avoid the racing issue among eSPI handshake process and driver probing. Chia-Wei Wang (4): dt-bindings: aspeed: Add eSPI controller MAINTAINER: Add ASPEED eSPI driver entry soc: aspeed: Add eSPI driver ARM: dts: aspeed: Add eSPI node .../devicetree/bindings/soc/aspeed/espi.yaml | 157 +++++ MAINTAINERS | 9 + arch/arm/boot/dts/aspeed-g6.dtsi | 17 + drivers/soc/aspeed/Kconfig | 11 + drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-espi-ctrl.c | 205 ++++++ drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 +++++++++ drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++ drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++ drivers/soc/aspeed/aspeed-espi-oob.h | 611 ++++++++++++++++++ drivers/soc/aspeed/aspeed-espi-perif.h | 539 +++++++++++++++ drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++ 12 files changed, 2529 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.h create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.h create mode 100644 drivers/soc/aspeed/aspeed-espi-ioc.h create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.h create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.h create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.h -- 2.17.1 From chiawei_wang at aspeedtech.com Thu Aug 26 16:16:21 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 26 Aug 2021 14:16:21 +0800 Subject: [PATCH v3 2/4] MAINTAINER: Add ASPEED eSPI driver entry In-Reply-To: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> Message-ID: <20210826061623.6352-3-chiawei_wang@aspeedtech.com> Add Ryan Chen and myself as the maintainer of Aspeed eSPI driver. Joel Stanley is added as the reviewer. Signed-off-by: Chia-Wei Wang --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index fd25e4ecf0b9..b21bcb46692e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1713,6 +1713,15 @@ F: drivers/crypto/axis F: drivers/mmc/host/usdhi6rol0.c F: drivers/pinctrl/pinctrl-artpec* +ARM/ASPEED ESPI DRIVER +M: Chia-Wei Wang +M: Ryan Chen +R: Joel Stanley +L: linux-aspeed at lists.ozlabs.org (moderated for non-subscribers) +L: openbmc at lists.ozlabs.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/soc/aspeed/espi.yaml + ARM/ASPEED I2C DRIVER M: Brendan Higgins R: Benjamin Herrenschmidt -- 2.17.1 From chiawei_wang at aspeedtech.com Thu Aug 26 16:16:20 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 26 Aug 2021 14:16:20 +0800 Subject: [PATCH v3 1/4] dt-bindings: aspeed: Add eSPI controller In-Reply-To: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> Message-ID: <20210826061623.6352-2-chiawei_wang@aspeedtech.com> Add dt-bindings for Aspeed eSPI controller Signed-off-by: Chia-Wei Wang --- .../devicetree/bindings/soc/aspeed/espi.yaml | 157 ++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml diff --git a/Documentation/devicetree/bindings/soc/aspeed/espi.yaml b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml new file mode 100644 index 000000000000..f8948efde379 --- /dev/null +++ b/Documentation/devicetree/bindings/soc/aspeed/espi.yaml @@ -0,0 +1,157 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# # Copyright (c) 2021 Aspeed Technology Inc. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/soc/aspeed/espi.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: Aspeed eSPI Controller + +maintainers: + - Chia-Wei Wang + - Ryan Chen + +description: + Aspeed eSPI controller implements a slave side eSPI endpoint device + supporting the four eSPI channels, namely peripheral, virtual wire, + out-of-band, and flash. + +properties: + compatible: + items: + - enum: + - aspeed,ast2500-espi + - aspeed,ast2600-espi + - const: simple-mfd + - const: syscon + + reg: + maxItems: 1 + + "#address-cells": + const: 1 + + "#size-cells": + const: 1 + + ranges: true + + espi-ctrl: + type: object + + properties: + compatible: + items: + - enum: + - aspeed,ast2500-espi-ctrl + - aspeed,ast2600-espi-ctrl + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + perif,memcyc-enable: + type: boolean + description: Enable memory cycle over eSPI peripheral channel + + perif,memcyc-src-addr: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The Host side address to be decoded into the memory cycle over eSPI peripheral channel + + perif,memcyc-size: + $ref: /schemas/types.yaml#/definitions/uint32 + description: The size of the memory region allocated for the memory cycle over eSPI peripheral channel + minimum: 65536 + + perif,dma-mode: + type: boolean + description: Enable DMA support for eSPI peripheral channel + + oob,dma-mode: + type: boolean + description: Enable DMA support for eSPI out-of-band channel + + oob,dma-tx-desc-num: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2 + maximum: 1023 + description: The number of TX descriptors available for eSPI OOB DMA engine + + oob,dma-rx-desc-num: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 2 + maximum: 1023 + description: The number of RX descriptors available for eSPI OOB DMA engine + + flash,dma-mode: + type: boolean + description: Enable DMA support for eSPI flash channel + + flash,safs-mode: + $ref: /schemas/types.yaml#/definitions/uint32 + enum: [ 0, 1, 2 ] + default: 0 + description: Slave-Attached-Sharing-Flash mode, 0->Mix, 1->SW, 2->HW + + dependencies: + perif,memcyc-src-addr: [ "perif,memcyc-enable" ] + perif,memcyc-size: [ "perif,memcyc-enable" ] + oob,dma-tx-desc-num: [ "oob,dma-mode" ] + oob,dma-rx-desc-num: [ "oob,dma-mode" ] + + required: + - compatible + - interrupts + - clocks + + espi-mmbi: + type: object + + properties: + compatible: + const: aspeed,ast2600-espi-mmbi + + interrupts: + maxItems: 1 + + required: + - compatible + - interrupts + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + - ranges + +additionalProperties: false + +examples: + - | + #include + #include + + espi: espi at 1e6ee000 { + compatible = "aspeed,ast2600-espi", "simple-mfd", "syscon"; + reg = <0x1e6ee000 0x1000>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x1e6ee000 0x1000>; + + espi_ctrl: espi-ctrl at 0 { + compatible = "aspeed,ast2600-espi-ctrl"; + reg = <0x0 0x800>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; + }; + + espi_mmbi: espi-mmbi at 800 { + compatible = "aspeed,ast2600-espi-mmbi"; + reg = <0x800 0x50>; + interrupts = ; + }; + }; -- 2.17.1 From chiawei_wang at aspeedtech.com Thu Aug 26 16:16:23 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 26 Aug 2021 14:16:23 +0800 Subject: [PATCH v3 4/4] ARM: dts: aspeed: Add eSPI node In-Reply-To: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> Message-ID: <20210826061623.6352-5-chiawei_wang@aspeedtech.com> Add eSPI to the device tree for Aspeed 5/6th generation SoCs. Signed-off-by: Chia-Wei Wang --- arch/arm/boot/dts/aspeed-g6.dtsi | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-g6.dtsi b/arch/arm/boot/dts/aspeed-g6.dtsi index f96607b7b4e2..47dc0b3993d1 100644 --- a/arch/arm/boot/dts/aspeed-g6.dtsi +++ b/arch/arm/boot/dts/aspeed-g6.dtsi @@ -364,6 +364,23 @@ status = "disabled"; }; + espi: espi at 1e6ee000 { + compatible = "aspeed,ast2600-espi", "simple-mfd", "syscon"; + reg = <0x1e6ee000 0x1000>; + + #address-cells = <1>; + #size-cells = <1>; + ranges = <0x0 0x1e6ee000 0x1000>; + + espi_ctrl: espi-ctrl at 0 { + compatible = "aspeed,ast2600-espi-ctrl"; + reg = <0x0 0x800>; + interrupts = ; + clocks = <&syscon ASPEED_CLK_GATE_ESPICLK>; + status = "disabled"; + }; + }; + gpio0: gpio at 1e780000 { #gpio-cells = <2>; gpio-controller; -- 2.17.1 From yangyingliang at huawei.com Mon Aug 23 23:00:43 2021 From: yangyingliang at huawei.com (Yang Yingliang) Date: Mon, 23 Aug 2021 21:00:43 +0800 Subject: [PATCH -next] soc: aspeed-lpc-ctrl: Fix missing clk_disable_unprepare() on error in aspeed_lpc_ctrl_probe() Message-ID: <20210823130043.1087204-1-yangyingliang@huawei.com> Fix the missing clk_disable_unprepare() before return from aspeed_lpc_ctrl_probe() in the error handling case. Fixes: 2f9b25fa6682 ("soc: aspeed: Re-enable FWH2AHB on AST2600") Reported-by: Hulk Robot Signed-off-by: Yang Yingliang --- drivers/soc/aspeed/aspeed-lpc-ctrl.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c index 6893c5ec3259..f4c989584d6b 100644 --- a/drivers/soc/aspeed/aspeed-lpc-ctrl.c +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c @@ -312,7 +312,8 @@ static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) lpc_ctrl->scu = syscon_regmap_lookup_by_compatible("aspeed,ast2600-scu"); if (IS_ERR(lpc_ctrl->scu)) { dev_err(dev, "couldn't find scu\n"); - return PTR_ERR(lpc_ctrl->scu); + rc = PTR_ERR(lpc_ctrl->scu); + goto err; } } -- 2.25.1 From chiawei_wang at aspeedtech.com Thu Aug 26 16:16:22 2021 From: chiawei_wang at aspeedtech.com (Chia-Wei Wang) Date: Thu, 26 Aug 2021 14:16:22 +0800 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> Message-ID: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> The Aspeed eSPI controller is slave device to communicate with the master through the Enhanced Serial Peripheral Interface (eSPI). All of the four eSPI channels, namely peripheral, virtual wire, out-of-band, and flash are supported. Signed-off-by: Chia-Wei Wang Reported-by: kernel test robot --- drivers/soc/aspeed/Kconfig | 11 + drivers/soc/aspeed/Makefile | 1 + drivers/soc/aspeed/aspeed-espi-ctrl.c | 205 +++++++++ drivers/soc/aspeed/aspeed-espi-ctrl.h | 304 ++++++++++++ drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++ drivers/soc/aspeed/aspeed-espi-ioc.h | 153 +++++++ drivers/soc/aspeed/aspeed-espi-oob.h | 611 +++++++++++++++++++++++++ drivers/soc/aspeed/aspeed-espi-perif.h | 539 ++++++++++++++++++++++ drivers/soc/aspeed/aspeed-espi-vw.h | 142 ++++++ 9 files changed, 2346 insertions(+) create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.c create mode 100644 drivers/soc/aspeed/aspeed-espi-ctrl.h create mode 100644 drivers/soc/aspeed/aspeed-espi-flash.h create mode 100644 drivers/soc/aspeed/aspeed-espi-ioc.h create mode 100644 drivers/soc/aspeed/aspeed-espi-oob.h create mode 100644 drivers/soc/aspeed/aspeed-espi-perif.h create mode 100644 drivers/soc/aspeed/aspeed-espi-vw.h diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index 243ca196e6ad..7617a02df5cf 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -42,6 +42,17 @@ config ASPEED_SOCINFO help Say yes to support decoding of ASPEED BMC information. +config ASPEED_ESPI + bool "ASPEED eSPI slave driver" + select REGMAP + select MFD_SYSCON + default n + help + Enable driver support for the Aspeed eSPI engine. The eSPI engine + plays as a slave device in BMC to communicate with the Host over + the eSPI interface. The four eSPI channels, namely peripheral, + virtual wire, out-of-band, and flash are supported. + endmenu endif diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile index fcab7192e1a4..650d8de2875d 100644 --- a/drivers/soc/aspeed/Makefile +++ b/drivers/soc/aspeed/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_ASPEED_P2A_CTRL) += aspeed-p2a-ctrl.o obj-$(CONFIG_ASPEED_SOCINFO) += aspeed-socinfo.o +obj-$(CONFIG_ASPEED_ESPI) += aspeed-espi-ctrl.o diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.c b/drivers/soc/aspeed/aspeed-espi-ctrl.c new file mode 100644 index 000000000000..2b95967d9d30 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-ctrl.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "aspeed-espi-ioc.h" +#include "aspeed-espi-ctrl.h" + +/* include alloc/free/event/ioctl handlers of the eSPI 4 channels */ +#include "aspeed-espi-perif.h" +#include "aspeed-espi-vw.h" +#include "aspeed-espi-oob.h" +#include "aspeed-espi-flash.h" + +#define DEVICE_NAME "aspeed-espi-ctrl" + +static irqreturn_t aspeed_espi_ctrl_isr(int irq, void *arg) +{ + uint32_t sts; + struct aspeed_espi_ctrl *espi_ctrl = (struct aspeed_espi_ctrl *)arg; + + regmap_read(espi_ctrl->map, ESPI_INT_STS, &sts); + + if (sts & ESPI_INT_STS_PERIF_BITS) { + aspeed_espi_perif_event(sts, espi_ctrl->perif); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_PERIF_BITS); + } + + if (sts & ESPI_INT_STS_VW_BITS) { + aspeed_espi_vw_event(sts, espi_ctrl->vw); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_VW_BITS); + } + + if (sts & (ESPI_INT_STS_OOB_BITS)) { + aspeed_espi_oob_event(sts, espi_ctrl->oob); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_OOB_BITS); + } + + if (sts & ESPI_INT_STS_FLASH_BITS) { + aspeed_espi_flash_event(sts, espi_ctrl->flash); + regmap_write(espi_ctrl->map, ESPI_INT_STS, sts & ESPI_INT_STS_FLASH_BITS); + } + + if (sts & ESPI_INT_STS_HW_RST_DEASSERT) { + aspeed_espi_perif_enable(espi_ctrl->perif); + aspeed_espi_vw_enable(espi_ctrl->vw); + aspeed_espi_oob_enable(espi_ctrl->oob); + aspeed_espi_flash_enable(espi_ctrl->flash); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1); + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1); + + if (espi_ctrl->version == ESPI_AST2500) + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T2, + ESPI_SYSEVT_INT_T2_HOST_RST_WARN | + ESPI_SYSEVT_INT_T2_OOB_RST_WARN); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_HW_RST_DEASSERT, + ESPI_INT_EN_HW_RST_DEASSERT); + + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT, + ESPI_SYSEVT_SLV_BOOT_STS | ESPI_SYSEVT_SLV_BOOT_DONE, + ESPI_SYSEVT_SLV_BOOT_STS | ESPI_SYSEVT_SLV_BOOT_DONE); + + regmap_write(espi_ctrl->map, ESPI_INT_STS, ESPI_INT_STS_HW_RST_DEASSERT); + } + + return IRQ_HANDLED; +} + +static int aspeed_espi_ctrl_probe(struct platform_device *pdev) +{ + int rc = 0; + struct aspeed_espi_ctrl *espi_ctrl; + struct device *dev = &pdev->dev; + + espi_ctrl = devm_kzalloc(dev, sizeof(*espi_ctrl), GFP_KERNEL); + if (!espi_ctrl) + return -ENOMEM; + + espi_ctrl->version = (uint32_t)of_device_get_match_data(dev); + + espi_ctrl->map = syscon_node_to_regmap(dev->parent->of_node); + if (IS_ERR(espi_ctrl->map)) { + dev_err(dev, "cannot get remap\n"); + return -ENODEV; + } + + espi_ctrl->irq = platform_get_irq(pdev, 0); + if (espi_ctrl->irq < 0) + return espi_ctrl->irq; + + espi_ctrl->clk = devm_clk_get(dev, NULL); + if (IS_ERR(espi_ctrl->clk)) { + dev_err(dev, "cannot get clock\n"); + return -ENODEV; + } + + rc = clk_prepare_enable(espi_ctrl->clk); + if (rc) { + dev_err(dev, "cannot enable clock\n"); + return rc; + } + + espi_ctrl->perif = aspeed_espi_perif_alloc(dev, espi_ctrl); + if (IS_ERR(espi_ctrl->perif)) { + dev_err(dev, "failed to allocate peripheral channel\n"); + return PTR_ERR(espi_ctrl->perif); + } + + espi_ctrl->vw = aspeed_espi_vw_alloc(dev, espi_ctrl); + if (IS_ERR(espi_ctrl->vw)) { + dev_err(dev, "failed to allocate virtual wire channel\n"); + return PTR_ERR(espi_ctrl->vw); + } + + espi_ctrl->oob = aspeed_espi_oob_alloc(dev, espi_ctrl); + if (IS_ERR(espi_ctrl->oob)) { + dev_err(dev, "failed to allocate out-of-band channel\n"); + return PTR_ERR(espi_ctrl->oob); + } + + espi_ctrl->flash = aspeed_espi_flash_alloc(dev, espi_ctrl); + if (rc) { + dev_err(dev, "failed to allocate flash channel\n"); + return PTR_ERR(espi_ctrl->flash); + } + + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0); + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1); + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1); + + rc = devm_request_irq(dev, espi_ctrl->irq, + aspeed_espi_ctrl_isr, + 0, DEVICE_NAME, espi_ctrl); + if (rc) { + dev_err(dev, "failed to request IRQ\n"); + return rc; + } + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_HW_RST_DEASSERT, + ESPI_INT_EN_HW_RST_DEASSERT); + + dev_set_drvdata(dev, espi_ctrl); + + dev_info(dev, "module loaded\n"); + + return 0; +} + +static int aspeed_espi_ctrl_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct aspeed_espi_ctrl *espi_ctrl = dev_get_drvdata(dev); + + aspeed_espi_perif_free(dev, espi_ctrl->perif); + aspeed_espi_vw_free(dev, espi_ctrl->vw); + aspeed_espi_oob_free(dev, espi_ctrl->oob); + aspeed_espi_flash_free(dev, espi_ctrl->flash); + + return 0; +} + +static const struct of_device_id aspeed_espi_ctrl_of_matches[] = { + { .compatible = "aspeed,ast2500-espi-ctrl", .data = (void *)ESPI_AST2500 }, + { .compatible = "aspeed,ast2600-espi-ctrl", .data = (void *)ESPI_AST2600 }, + { }, +}; + +static struct platform_driver aspeed_espi_ctrl_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_espi_ctrl_of_matches, + }, + .probe = aspeed_espi_ctrl_probe, + .remove = aspeed_espi_ctrl_remove, +}; + +module_platform_driver(aspeed_espi_ctrl_driver); + +MODULE_AUTHOR("Chia-Wei Wang "); +MODULE_AUTHOR("Ryan Chen "); +MODULE_DESCRIPTION("Control of Aspeed eSPI Slave Device"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/soc/aspeed/aspeed-espi-ctrl.h b/drivers/soc/aspeed/aspeed-espi-ctrl.h new file mode 100644 index 000000000000..44b93698fd0f --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-ctrl.h @@ -0,0 +1,304 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#ifndef _ASPEED_ESPI_CTRL_H_ +#define _ASPEED_ESPI_CTRL_H_ + +#include + +enum aspeed_espi_version { + ESPI_AST2500, + ESPI_AST2600, +}; + +struct aspeed_espi_ctrl { + struct device *dev; + + struct regmap *map; + struct clk *clk; + + int irq; + + struct aspeed_espi_perif *perif; + struct aspeed_espi_vw *vw; + struct aspeed_espi_oob *oob; + struct aspeed_espi_flash *flash; + + uint32_t version; +}; + +/* eSPI register offset */ +#define ESPI_CTRL 0x000 +#define ESPI_CTRL_OOB_RX_SW_RST BIT(28) +#define ESPI_CTRL_FLASH_TX_DMA_EN BIT(23) +#define ESPI_CTRL_FLASH_RX_DMA_EN BIT(22) +#define ESPI_CTRL_OOB_TX_DMA_EN BIT(21) +#define ESPI_CTRL_OOB_RX_DMA_EN BIT(20) +#define ESPI_CTRL_PERIF_NP_TX_DMA_EN BIT(19) +#define ESPI_CTRL_PERIF_PC_TX_DMA_EN BIT(17) +#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) +#define ESPI_CTRL_FLASH_SW_MODE_MASK GENMASK(11, 10) +#define ESPI_CTRL_FLASH_SW_MODE_SHIFT 10 +#define ESPI_CTRL_PERIF_PC_RX_DMA_EN BIT(16) +#define ESPI_CTRL_FLASH_SW_RDY BIT(7) +#define ESPI_CTRL_OOB_SW_RDY BIT(4) +#define ESPI_CTRL_VW_SW_RDY BIT(3) +#define ESPI_CTRL_PERIF_SW_RDY BIT(1) +#define ESPI_STS 0x004 +#define ESPI_INT_STS 0x008 +#define ESPI_INT_STS_HW_RST_DEASSERT BIT(31) +#define ESPI_INT_STS_OOB_RX_TMOUT BIT(23) +#define ESPI_INT_STS_VW_SYSEVT1 BIT(22) +#define ESPI_INT_STS_FLASH_TX_ERR BIT(21) +#define ESPI_INT_STS_OOB_TX_ERR BIT(20) +#define ESPI_INT_STS_FLASH_TX_ABT BIT(19) +#define ESPI_INT_STS_OOB_TX_ABT BIT(18) +#define ESPI_INT_STS_PERIF_NP_TX_ABT BIT(17) +#define ESPI_INT_STS_PERIF_PC_TX_ABT BIT(16) +#define ESPI_INT_STS_FLASH_RX_ABT BIT(15) +#define ESPI_INT_STS_OOB_RX_ABT BIT(14) +#define ESPI_INT_STS_PERIF_NP_RX_ABT BIT(13) +#define ESPI_INT_STS_PERIF_PC_RX_ABT BIT(12) +#define ESPI_INT_STS_PERIF_NP_TX_ERR BIT(11) +#define ESPI_INT_STS_PERIF_PC_TX_ERR BIT(10) +#define ESPI_INT_STS_VW_GPIOEVT BIT(9) +#define ESPI_INT_STS_VW_SYSEVT BIT(8) +#define ESPI_INT_STS_FLASH_TX_CMPLT BIT(7) +#define ESPI_INT_STS_FLASH_RX_CMPLT BIT(6) +#define ESPI_INT_STS_OOB_TX_CMPLT BIT(5) +#define ESPI_INT_STS_OOB_RX_CMPLT BIT(4) +#define ESPI_INT_STS_PERIF_NP_TX_CMPLT BIT(3) +#define ESPI_INT_STS_PERIF_PC_TX_CMPLT BIT(1) +#define ESPI_INT_STS_PERIF_PC_RX_CMPLT BIT(0) +#define ESPI_INT_EN 0x00c +#define ESPI_INT_EN_HW_RST_DEASSERT BIT(31) +#define ESPI_INT_EN_OOB_RX_TMOUT BIT(23) +#define ESPI_INT_EN_VW_SYSEVT1 BIT(22) +#define ESPI_INT_EN_FLASH_TX_ERR BIT(21) +#define ESPI_INT_EN_OOB_TX_ERR BIT(20) +#define ESPI_INT_EN_FLASH_TX_ABT BIT(19) +#define ESPI_INT_EN_OOB_TX_ABT BIT(18) +#define ESPI_INT_EN_PERIF_NP_TX_ABT BIT(17) +#define ESPI_INT_EN_PERIF_PC_TX_ABT BIT(16) +#define ESPI_INT_EN_FLASH_RX_ABT BIT(15) +#define ESPI_INT_EN_OOB_RX_ABT BIT(14) +#define ESPI_INT_EN_PERIF_NP_RX_ABT BIT(13) +#define ESPI_INT_EN_PERIF_PC_RX_ABT BIT(12) +#define ESPI_INT_EN_PERIF_NP_TX_ERR BIT(11) +#define ESPI_INT_EN_PERIF_PC_TX_ERR BIT(10) +#define ESPI_INT_EN_VW_GPIOEVT BIT(9) +#define ESPI_INT_EN_VW_SYSEVT BIT(8) +#define ESPI_INT_EN_FLASH_TX_CMPLT BIT(7) +#define ESPI_INT_EN_FLASH_RX_CMPLT BIT(6) +#define ESPI_INT_EN_OOB_TX_CMPLT BIT(5) +#define ESPI_INT_EN_OOB_RX_CMPLT BIT(4) +#define ESPI_INT_EN_PERIF_NP_TX_CMPLT BIT(3) +#define ESPI_INT_EN_PERIF_PC_TX_CMPLT BIT(1) +#define ESPI_INT_EN_PERIF_PC_RX_CMPLT BIT(0) +#define ESPI_PERIF_PC_RX_DMA 0x010 +#define ESPI_PERIF_PC_RX_CTRL 0x014 +#define ESPI_PERIF_PC_RX_CTRL_PEND_SERV BIT(31) +#define ESPI_PERIF_PC_RX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT 12 +#define ESPI_PERIF_PC_RX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT 8 +#define ESPI_PERIF_PC_RX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT 0 +#define ESPI_PERIF_PC_RX_PORT 0x018 +#define ESPI_PERIF_PC_TX_DMA 0x020 +#define ESPI_PERIF_PC_TX_CTRL 0x024 +#define ESPI_PERIF_PC_TX_CTRL_TRIGGER BIT(31) +#define ESPI_PERIF_PC_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_PERIF_PC_TX_CTRL_LEN_SHIFT 12 +#define ESPI_PERIF_PC_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_PERIF_PC_TX_CTRL_TAG_SHIFT 8 +#define ESPI_PERIF_PC_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_PERIF_PC_TX_CTRL_CYC_SHIFT 0 +#define ESPI_PERIF_PC_TX_PORT 0x028 +#define ESPI_PERIF_NP_TX_DMA 0x030 +#define ESPI_PERIF_NP_TX_CTRL 0x034 +#define ESPI_PERIF_NP_TX_CTRL_TRIGGER BIT(31) +#define ESPI_PERIF_NP_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_PERIF_NP_TX_CTRL_LEN_SHIFT 12 +#define ESPI_PERIF_NP_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_PERIF_NP_TX_CTRL_TAG_SHIFT 8 +#define ESPI_PERIF_NP_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_PERIF_NP_TX_CTRL_CYC_SHIFT 0 +#define ESPI_PERIF_NP_TX_PORT 0x038 +#define ESPI_OOB_RX_DMA 0x040 +#define ESPI_OOB_RX_CTRL 0x044 +#define ESPI_OOB_RX_CTRL_PEND_SERV BIT(31) +#define ESPI_OOB_RX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_OOB_RX_CTRL_LEN_SHIFT 12 +#define ESPI_OOB_RX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_OOB_RX_CTRL_TAG_SHIFT 8 +#define ESPI_OOB_RX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_OOB_RX_CTRL_CYC_SHIFT 0 +#define ESPI_OOB_RX_PORT 0x048 +#define ESPI_OOB_TX_DMA 0x050 +#define ESPI_OOB_TX_CTRL 0x054 +#define ESPI_OOB_TX_CTRL_TRIGGER BIT(31) +#define ESPI_OOB_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_OOB_TX_CTRL_LEN_SHIFT 12 +#define ESPI_OOB_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_OOB_TX_CTRL_TAG_SHIFT 8 +#define ESPI_OOB_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_OOB_TX_CTRL_CYC_SHIFT 0 +#define ESPI_OOB_TX_PORT 0x058 +#define ESPI_FLASH_RX_DMA 0x060 +#define ESPI_FLASH_RX_CTRL 0x064 +#define ESPI_FLASH_RX_CTRL_PEND_SERV BIT(31) +#define ESPI_FLASH_RX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_FLASH_RX_CTRL_LEN_SHIFT 12 +#define ESPI_FLASH_RX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_FLASH_RX_CTRL_TAG_SHIFT 8 +#define ESPI_FLASH_RX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_FLASH_RX_CTRL_CYC_SHIFT 0 +#define ESPI_FLASH_RX_PORT 0x068 +#define ESPI_FLASH_TX_DMA 0x070 +#define ESPI_FLASH_TX_CTRL 0x074 +#define ESPI_FLASH_TX_CTRL_TRIGGER BIT(31) +#define ESPI_FLASH_TX_CTRL_LEN_MASK GENMASK(23, 12) +#define ESPI_FLASH_TX_CTRL_LEN_SHIFT 12 +#define ESPI_FLASH_TX_CTRL_TAG_MASK GENMASK(11, 8) +#define ESPI_FLASH_TX_CTRL_TAG_SHIFT 8 +#define ESPI_FLASH_TX_CTRL_CYC_MASK GENMASK(7, 0) +#define ESPI_FLASH_TX_CTRL_CYC_SHIFT 0 +#define ESPI_FLASH_TX_PORT 0x078 +#define ESPI_CTRL2 0x080 +#define ESPI_CTRL2_MEMCYC_RD_DIS BIT(6) +#define ESPI_CTRL2_MEMCYC_WR_DIS BIT(4) +#define ESPI_PERIF_PC_RX_SADDR 0x084 +#define ESPI_PERIF_PC_RX_TADDR 0x088 +#define ESPI_PERIF_PC_RX_MASK 0x08c +#define ESPI_PERIF_PC_RX_MASK_CFG_WP BIT(0) +#define ESPI_SYSEVT_INT_EN 0x094 +#define ESPI_SYSEVT 0x098 +#define ESPI_SYSEVT_HOST_RST_ACK BIT(27) +#define ESPI_SYSEVT_RST_CPU_INIT BIT(26) +#define ESPI_SYSEVT_SLV_BOOT_STS BIT(23) +#define ESPI_SYSEVT_NON_FATAL_ERR BIT(22) +#define ESPI_SYSEVT_FATAL_ERR BIT(21) +#define ESPI_SYSEVT_SLV_BOOT_DONE BIT(20) +#define ESPI_SYSEVT_OOB_RST_ACK BIT(16) +#define ESPI_SYSEVT_NMI_OUT BIT(10) +#define ESPI_SYSEVT_SMI_OUT BIT(9) +#define ESPI_SYSEVT_HOST_RST_WARN BIT(8) +#define ESPI_SYSEVT_OOB_RST_WARN BIT(6) +#define ESPI_SYSEVT_PLTRSTN BIT(5) +#define ESPI_SYSEVT_SUSPEND BIT(4) +#define ESPI_SYSEVT_S5_SLEEP BIT(2) +#define ESPI_SYSEVT_S4_SLEEP BIT(1) +#define ESPI_SYSEVT_S3_SLEEP BIT(0) +#define ESPI_VW_GPIO_VAL 0x09c +#define ESPI_GEN_CAP_N_CONF 0x0a0 +#define ESPI_CH0_CAP_N_CONF 0x0a4 +#define ESPI_CH1_CAP_N_CONF 0x0a8 +#define ESPI_CH2_CAP_N_CONF 0x0ac +#define ESPI_CH3_CAP_N_CONF 0x0b0 +#define ESPI_CH3_CAP_N_CONF2 0x0b4 +#define ESPI_SYSEVT1_INT_EN 0x100 +#define ESPI_SYSEVT1 0x104 +#define ESPI_SYSEVT1_SUSPEND_ACK BIT(20) +#define ESPI_SYSEVT1_SUSPEND_WARN BIT(0) +#define ESPI_SYSEVT_INT_T0 0x110 +#define ESPI_SYSEVT_INT_T1 0x114 +#define ESPI_SYSEVT_INT_T2 0x118 +#define ESPI_SYSEVT_INT_T2_HOST_RST_WARN ESPI_SYSEVT_HOST_RST_WARN +#define ESPI_SYSEVT_INT_T2_OOB_RST_WARN ESPI_SYSEVT_OOB_RST_WARN +#define ESPI_SYSEVT_INT_STS 0x11c +#define ESPI_SYSEVT_INT_STS_NMI_OUT ESPI_SYSEVT_NMI_OUT +#define ESPI_SYSEVT_INT_STS_SMI_OUT ESPI_SYSEVT_SMI_OUT +#define ESPI_SYSEVT_INT_STS_HOST_RST_WARN ESPI_SYSEVT_HOST_RST_WARN +#define ESPI_SYSEVT_INT_STS_OOB_RST_WARN ESPI_SYSEVT_OOB_RST_WARN +#define ESPI_SYSEVT_INT_STS_PLTRSTN ESPI_SYSEVT_PLTRSTN +#define ESPI_SYSEVT_INT_STS_SUSPEND ESPI_SYSEVT_SUSPEND +#define ESPI_SYSEVT_INT_STS_S5_SLEEP ESPI_SYSEVT_INT_S5_SLEEP +#define ESPI_SYSEVT_INT_STS_S4_SLEEP ESPI_SYSEVT_INT_S4_SLEEP +#define ESPI_SYSEVT_INT_STS_S3_SLEEP ESPI_SYSEVT_INT_S3_SLEEP +#define ESPI_SYSEVT1_INT_T0 0x120 +#define ESPI_SYSEVT1_INT_T1 0x124 +#define ESPI_SYSEVT1_INT_T2 0x128 +#define ESPI_SYSEVT1_INT_STS 0x12c +#define ESPI_SYSEVT1_INT_STS_SUSPEND_WARN ESPI_SYSEVT1_SUSPEND_WARN +#define ESPI_OOB_RX_DMA_RB_SIZE 0x130 +#define ESPI_OOB_RX_DMA_RD_PTR 0x134 +#define ESPI_OOB_RX_DMA_RD_PTR_UPDATE BIT(31) +#define ESPI_OOB_RX_DMA_WS_PTR 0x138 +#define ESPI_OOB_RX_DMA_WS_PTR_RECV_EN BIT(31) +#define ESPI_OOB_RX_DMA_WS_PTR_SP_MASK GENMASK(27, 16) +#define ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT 16 +#define ESPI_OOB_RX_DMA_WS_PTR_WP_MASK GENMASK(11, 0) +#define ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT 0 +#define ESPI_OOB_TX_DMA_RB_SIZE 0x140 +#define ESPI_OOB_TX_DMA_RD_PTR 0x144 +#define ESPI_OOB_TX_DMA_RD_PTR_UPDATE BIT(31) +#define ESPI_OOB_TX_DMA_WR_PTR 0x148 +#define ESPI_OOB_TX_DMA_WR_PTR_SEND_EN BIT(31) + +/* collect ESPI_INT_STS bits of eSPI channels for convenience */ +#define ESPI_INT_STS_PERIF_BITS \ + (ESPI_INT_STS_PERIF_NP_TX_ABT | \ + ESPI_INT_STS_PERIF_PC_TX_ABT | \ + ESPI_INT_STS_PERIF_NP_RX_ABT | \ + ESPI_INT_STS_PERIF_PC_RX_ABT | \ + ESPI_INT_STS_PERIF_NP_TX_ERR | \ + ESPI_INT_STS_PERIF_PC_TX_ERR | \ + ESPI_INT_STS_PERIF_NP_TX_CMPLT | \ + ESPI_INT_STS_PERIF_PC_TX_CMPLT | \ + ESPI_INT_STS_PERIF_PC_RX_CMPLT) + +#define ESPI_INT_STS_VW_BITS \ + (ESPI_INT_STS_VW_SYSEVT1 | \ + ESPI_INT_STS_VW_GPIOEVT | \ + ESPI_INT_STS_VW_SYSEVT) + +#define ESPI_INT_STS_OOB_BITS \ + (ESPI_INT_STS_OOB_RX_TMOUT | \ + ESPI_INT_STS_OOB_TX_ERR | \ + ESPI_INT_STS_OOB_TX_ABT | \ + ESPI_INT_STS_OOB_RX_ABT | \ + ESPI_INT_STS_OOB_TX_CMPLT | \ + ESPI_INT_STS_OOB_RX_CMPLT) + +#define ESPI_INT_STS_FLASH_BITS \ + (ESPI_INT_STS_FLASH_TX_ERR | \ + ESPI_INT_STS_FLASH_TX_ABT | \ + ESPI_INT_STS_FLASH_RX_ABT | \ + ESPI_INT_STS_FLASH_TX_CMPLT | \ + ESPI_INT_STS_FLASH_RX_CMPLT) + +/* collect ESPI_INT_EN bits of eSPI channels for convenience */ +#define ESPI_INT_EN_PERIF_BITS \ + (ESPI_INT_EN_PERIF_NP_TX_ABT | \ + ESPI_INT_EN_PERIF_PC_TX_ABT | \ + ESPI_INT_EN_PERIF_NP_RX_ABT | \ + ESPI_INT_EN_PERIF_PC_RX_ABT | \ + ESPI_INT_EN_PERIF_NP_TX_ERR | \ + ESPI_INT_EN_PERIF_PC_TX_ERR | \ + ESPI_INT_EN_PERIF_NP_TX_CMPLT | \ + ESPI_INT_EN_PERIF_PC_TX_CMPLT | \ + ESPI_INT_EN_PERIF_PC_RX_CMPLT) + +#define ESPI_INT_EN_VW_BITS \ + (ESPI_INT_EN_VW_SYSEVT1 | \ + ESPI_INT_EN_VW_GPIOEVT | \ + ESPI_INT_EN_VW_SYSEVT) + +#define ESPI_INT_EN_OOB_BITS \ + (ESPI_INT_EN_OOB_RX_TMOUT | \ + ESPI_INT_EN_OOB_TX_ERR | \ + ESPI_INT_EN_OOB_TX_ABT | \ + ESPI_INT_EN_OOB_RX_ABT | \ + ESPI_INT_EN_OOB_TX_CMPLT | \ + ESPI_INT_EN_OOB_RX_CMPLT) + +#define ESPI_INT_EN_FLASH_BITS \ + (ESPI_INT_EN_FLASH_TX_ERR | \ + ESPI_INT_EN_FLASH_TX_ABT | \ + ESPI_INT_EN_FLASH_RX_ABT | \ + ESPI_INT_EN_FLASH_TX_CMPLT | \ + ESPI_INT_EN_FLASH_RX_CMPLT) + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-flash.h b/drivers/soc/aspeed/aspeed-espi-flash.h new file mode 100644 index 000000000000..1acb9877cb2c --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-flash.h @@ -0,0 +1,380 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 ASPEED Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI flash channel. It is part of Aspeed eSPI slave + * driver implementation + */ +#ifndef _ASPEED_ESPI_FLASH_H_ +#define _ASPEED_ESPI_FLASH_H_ + +#define FLASH_MDEV_NAME "aspeed-espi-flash" + +enum aspeed_espi_flash_safs_mode { + SAFS_MODE_MIX, + SAFS_MODE_SW, + SAFS_MODE_HW, + SAFS_MODES, +}; + +struct aspeed_espi_flash_dma { + void *tx_virt; + dma_addr_t tx_addr; + void *rx_virt; + dma_addr_t rx_addr; +}; + +struct aspeed_espi_flash { + uint32_t safs_mode; + + uint32_t dma_mode; + struct aspeed_espi_flash_dma dma; + + uint32_t rx_ready; + wait_queue_head_t wq; + + struct mutex get_rx_mtx; + struct mutex put_tx_mtx; + + spinlock_t lock; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +static long aspeed_espi_flash_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_flash *espi_flash) +{ + int i, rc = 0; + unsigned long flags; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl; + + if (fp->f_flags & O_NONBLOCK) { + if (mutex_trylock(&espi_flash->get_rx_mtx)) + return -EBUSY; + + if (!espi_flash->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { + mutex_lock(&espi_flash->get_rx_mtx); + + if (!espi_flash->rx_ready) { + rc = wait_event_interruptible(espi_flash->wq, + espi_flash->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; + } + } + } + + /* common header (i.e. cycle type, tag, and length) is taken by HW */ + regmap_read(espi_ctrl->map, ESPI_FLASH_RX_CTRL, ®); + cyc = (reg & ESPI_FLASH_RX_CTRL_CYC_MASK) >> ESPI_FLASH_RX_CTRL_CYC_SHIFT; + tag = (reg & ESPI_FLASH_RX_CTRL_TAG_MASK) >> ESPI_FLASH_RX_CTRL_TAG_SHIFT; + len = (reg & ESPI_FLASH_RX_CTRL_LEN_MASK) >> ESPI_FLASH_RX_CTRL_LEN_SHIFT; + + /* + * calculate the length of the rest part of the + * eSPI packet to be read from HW and copied to + * user space. + */ + switch (cyc) { + case ESPI_FLASH_READ: + case ESPI_FLASH_WRITE: + case ESPI_FLASH_ERASE: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_flash_rwe); + break; + case ESPI_FLASH_SUC_CMPLT_D_MIDDLE: + case ESPI_FLASH_SUC_CMPLT_D_FIRST: + case ESPI_FLASH_SUC_CMPLT_D_LAST: + case ESPI_FLASH_SUC_CMPLT_D_ONLY: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_flash_cmplt); + break; + case ESPI_FLASH_SUC_CMPLT: + case ESPI_FLASH_UNSUC_CMPLT: + pkt_len = len + sizeof(struct espi_flash_cmplt); + break; + default: + rc = -EFAULT; + goto unlock_mtx_n_out; + } + + if (ioc->pkt_len < pkt_len) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = cyc; + hdr->tag = tag; + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + + if (espi_flash->dma_mode) { + memcpy(hdr + 1, espi_flash->dma.rx_virt, + pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) { + regmap_read(espi_ctrl->map, + ESPI_FLASH_RX_PORT, ®); + pkt[i] = reg & 0xff; + } + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + spin_lock_irqsave(&espi_flash->lock, flags); + + regmap_write_bits(espi_ctrl->map, ESPI_FLASH_RX_CTRL, + ESPI_FLASH_RX_CTRL_PEND_SERV, + ESPI_FLASH_RX_CTRL_PEND_SERV); + + espi_flash->rx_ready = 0; + + spin_unlock_irqrestore(&espi_flash->lock, flags); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_flash->get_rx_mtx); + + return rc; +} + +static long aspeed_espi_flash_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_flash *espi_flash) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl; + + if (!mutex_trylock(&espi_flash->put_tx_mtx)) + return -EAGAIN; + + regmap_read(espi_ctrl->map, ESPI_FLASH_TX_CTRL, ®); + if (reg & ESPI_FLASH_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_flash->dma_mode) { + memcpy(espi_flash->dma.tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_FLASH_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_FLASH_TX_CTRL_CYC_SHIFT) & ESPI_FLASH_TX_CTRL_CYC_MASK) + | ((tag << ESPI_FLASH_TX_CTRL_TAG_SHIFT) & ESPI_FLASH_TX_CTRL_TAG_MASK) + | ((len << ESPI_FLASH_TX_CTRL_LEN_SHIFT) & ESPI_FLASH_TX_CTRL_LEN_MASK) + | ESPI_FLASH_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_FLASH_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_flash->put_tx_mtx); + + return rc; +} + +static long aspeed_espi_flash_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct aspeed_espi_ioc ioc; + struct aspeed_espi_flash *espi_flash = container_of( + fp->private_data, + struct aspeed_espi_flash, + mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; + + if (ioc.pkt_len > ESPI_PKT_LEN_MAX) + return -EINVAL; + + switch (cmd) { + case ASPEED_ESPI_FLASH_GET_RX: + return aspeed_espi_flash_get_rx(fp, &ioc, espi_flash); + case ASPEED_ESPI_FLASH_PUT_TX: + return aspeed_espi_flash_put_tx(fp, &ioc, espi_flash); + }; + + return -EINVAL; +} + +static const struct file_operations aspeed_espi_flash_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aspeed_espi_flash_ioctl, +}; + +static void aspeed_espi_flash_event(uint32_t sts, struct aspeed_espi_flash *espi_flash) +{ + unsigned long flags; + + if (sts & ESPI_INT_STS_FLASH_RX_CMPLT) { + spin_lock_irqsave(&espi_flash->lock, flags); + espi_flash->rx_ready = 1; + spin_unlock_irqrestore(&espi_flash->lock, flags); + wake_up_interruptible(&espi_flash->wq); + } +} + +static void aspeed_espi_flash_enable(struct aspeed_espi_flash *espi_flash) +{ + struct aspeed_espi_flash_dma *dma = &espi_flash->dma; + struct aspeed_espi_ctrl *espi_ctrl = espi_flash->ctrl; + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_FLASH_SW_MODE_MASK, + (espi_flash->safs_mode << ESPI_CTRL_FLASH_SW_MODE_SHIFT)); + + if (espi_flash->dma_mode) { + regmap_write(espi_ctrl->map, ESPI_FLASH_TX_DMA, dma->tx_addr); + regmap_write(espi_ctrl->map, ESPI_FLASH_RX_DMA, dma->rx_addr); + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_FLASH_TX_DMA_EN | ESPI_CTRL_FLASH_RX_DMA_EN, + ESPI_CTRL_FLASH_TX_DMA_EN | ESPI_CTRL_FLASH_RX_DMA_EN); + } + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_FLASH_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_FLASH_BITS, + ESPI_INT_EN_FLASH_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_FLASH_SW_RDY, + ESPI_CTRL_FLASH_SW_RDY); +} + +static void *aspeed_espi_flash_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc = 0; + struct aspeed_espi_flash *espi_flash; + struct aspeed_espi_flash_dma *dma; + + espi_flash = devm_kzalloc(dev, sizeof(*espi_flash), GFP_KERNEL); + if (!espi_flash) + return ERR_PTR(-ENOMEM); + + espi_flash->ctrl = espi_ctrl; + + init_waitqueue_head(&espi_flash->wq); + + spin_lock_init(&espi_flash->lock); + + mutex_init(&espi_flash->put_tx_mtx); + mutex_init(&espi_flash->get_rx_mtx); + + if (of_property_read_bool(dev->of_node, "flash,dma-mode")) + espi_flash->dma_mode = 1; + + of_property_read_u32(dev->of_node, "flash,safs-mode", &espi_flash->safs_mode); + if (espi_flash->safs_mode >= SAFS_MODES) { + dev_err(dev, "invalid SAFS mode\n"); + return ERR_PTR(-EINVAL); + } + + if (espi_flash->dma_mode) { + dma = &espi_flash->dma; + + dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->tx_addr, GFP_KERNEL); + if (!dma->tx_virt) { + dev_err(dev, "cannot allocate DMA TX buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->rx_addr, GFP_KERNEL); + if (!dma->rx_virt) { + dev_err(dev, "cannot allocate DMA RX buffer\n"); + return ERR_PTR(-ENOMEM); + } + } + + espi_flash->mdev.parent = dev; + espi_flash->mdev.minor = MISC_DYNAMIC_MINOR; + espi_flash->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", FLASH_MDEV_NAME); + espi_flash->mdev.fops = &aspeed_espi_flash_fops; + rc = misc_register(&espi_flash->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_flash_enable(espi_flash); + + return espi_flash; +} + +static void aspeed_espi_flash_free(struct device *dev, struct aspeed_espi_flash *espi_flash) +{ + struct aspeed_espi_flash_dma *dma = &espi_flash->dma; + + if (espi_flash->dma_mode) { + dma_free_coherent(dev, PAGE_SIZE, dma->tx_virt, dma->tx_addr); + dma_free_coherent(dev, PAGE_SIZE, dma->rx_virt, dma->rx_addr); + } + + mutex_destroy(&espi_flash->put_tx_mtx); + mutex_destroy(&espi_flash->get_rx_mtx); + + misc_deregister(&espi_flash->mdev); +} + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-ioc.h b/drivers/soc/aspeed/aspeed-espi-ioc.h new file mode 100644 index 000000000000..e93739c80b5f --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-ioc.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Aspeed Technology Inc. + */ +#ifndef _ASPEED_ESPI_IOC_H +#define _ASPEED_ESPI_IOC_H + +#include +#include + +/* + * eSPI cycle type encoding + * + * Section 5.1 Cycle Types and Packet Format, + * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. + */ +#define ESPI_PERIF_MEMRD32 0x00 +#define ESPI_PERIF_MEMRD64 0x02 +#define ESPI_PERIF_MEMWR32 0x01 +#define ESPI_PERIF_MEMWR64 0x03 +#define ESPI_PERIF_MSG 0x10 +#define ESPI_PERIF_MSG_D 0x11 +#define ESPI_PERIF_SUC_CMPLT 0x06 +#define ESPI_PERIF_SUC_CMPLT_D_MIDDLE 0x09 +#define ESPI_PERIF_SUC_CMPLT_D_FIRST 0x0b +#define ESPI_PERIF_SUC_CMPLT_D_LAST 0x0d +#define ESPI_PERIF_SUC_CMPLT_D_ONLY 0x0f +#define ESPI_PERIF_UNSUC_CMPLT 0x0c +#define ESPI_OOB_MSG 0x21 +#define ESPI_FLASH_READ 0x00 +#define ESPI_FLASH_WRITE 0x01 +#define ESPI_FLASH_ERASE 0x02 +#define ESPI_FLASH_SUC_CMPLT 0x06 +#define ESPI_FLASH_SUC_CMPLT_D_MIDDLE 0x09 +#define ESPI_FLASH_SUC_CMPLT_D_FIRST 0x0b +#define ESPI_FLASH_SUC_CMPLT_D_LAST 0x0d +#define ESPI_FLASH_SUC_CMPLT_D_ONLY 0x0f +#define ESPI_FLASH_UNSUC_CMPLT 0x0c + +/* + * eSPI packet format structure + * + * Section 5.1 Cycle Types and Packet Format, + * Intel eSPI Interface Base Specification, Rev 1.0, Jan. 2016. + */ +struct espi_comm_hdr { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; +}; + +struct espi_perif_mem32 { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint32_t addr_be; + uint8_t data[]; +} __packed; + +struct espi_perif_mem64 { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint32_t addr_be; + uint8_t data[]; +} __packed; + +struct espi_perif_msg { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t msg_code; + uint8_t msg_byte[4]; + uint8_t data[]; +} __packed; + +struct espi_perif_cmplt { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t data[]; +} __packed; + +struct espi_oob_msg { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t data[]; +}; + +struct espi_flash_rwe { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint32_t addr_be; + uint8_t data[]; +} __packed; + +struct espi_flash_cmplt { + uint8_t cyc; + uint8_t len_h : 4; + uint8_t tag : 4; + uint8_t len_l; + uint8_t data[]; +} __packed; + +struct aspeed_espi_ioc { + uint32_t pkt_len; + uint8_t *pkt; +}; + +/* + * we choose the longest header and the max payload size + * based on the Intel specification to define the maximum + * eSPI packet length + */ +#define ESPI_PLD_LEN_MIN (1UL << 6) +#define ESPI_PLD_LEN_MAX (1UL << 12) +#define ESPI_PKT_LEN_MAX (sizeof(struct espi_perif_msg) + ESPI_PLD_LEN_MAX) + +#define __ASPEED_ESPI_IOCTL_MAGIC 0xb8 + +/* peripheral channel (ch0) */ +#define ASPEED_ESPI_PERIF_PC_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x00, struct aspeed_espi_ioc) +#define ASPEED_ESPI_PERIF_PC_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x01, struct aspeed_espi_ioc) +#define ASPEED_ESPI_PERIF_NP_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x02, struct aspeed_espi_ioc) +/* peripheral channel (ch1) */ +#define ASPEED_ESPI_VW_GET_GPIO_VAL _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x10, uint8_t) +#define ASPEED_ESPI_VW_PUT_GPIO_VAL _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x11, uint8_t) +/* out-of-band channel (ch2) */ +#define ASPEED_ESPI_OOB_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x20, struct aspeed_espi_ioc) +#define ASPEED_ESPI_OOB_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x21, struct aspeed_espi_ioc) +/* flash channel (ch3) */ +#define ASPEED_ESPI_FLASH_GET_RX _IOR(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x30, struct aspeed_espi_ioc) +#define ASPEED_ESPI_FLASH_PUT_TX _IOW(__ASPEED_ESPI_IOCTL_MAGIC, \ + 0x31, struct aspeed_espi_ioc) + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-oob.h b/drivers/soc/aspeed/aspeed-espi-oob.h new file mode 100644 index 000000000000..182a9ca1e4cc --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-oob.h @@ -0,0 +1,611 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 Aspeed Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI out-of-band channel. It is part of Aspeed eSPI + * slave driver implementation + */ +#ifndef _ASPEED_ESPI_OOB_H_ +#define _ASPEED_ESPI_OOB_H_ + +#define OOB_MDEV_NAME "aspeed-espi-oob" + +/* DMA descriptor is supported since AST2600 */ +#define OOB_DMA_DESC_MAX_NUM 1024 + +/* DMA TX descriptor type */ +#define OOB_DMA_TX_DESC_CUST 0x04 + +struct oob_tx_dma_desc { + uint32_t data_addr; + uint8_t cyc; + uint16_t tag : 4; + uint16_t len : 12; + uint8_t msg_type : 3; + uint8_t raz0 : 1; + uint8_t pec : 1; + uint8_t int_en : 1; + uint8_t pause : 1; + uint8_t raz1 : 1; + uint32_t raz2; + uint32_t raz3; +} __packed; + +struct oob_rx_dma_desc { + uint32_t data_addr; + uint8_t cyc; + uint16_t tag : 4; + uint16_t len : 12; + uint8_t raz : 7; + uint8_t dirty : 1; +} __packed; + +struct aspeed_espi_oob_dma { + uint32_t tx_desc_num; + uint32_t rx_desc_num; + + struct oob_tx_dma_desc *tx_desc; + dma_addr_t tx_desc_addr; + + struct oob_rx_dma_desc *rx_desc; + dma_addr_t rx_desc_addr; + + void *tx_virt; + dma_addr_t tx_addr; + + void *rx_virt; + dma_addr_t rx_addr; +}; + +struct aspeed_espi_oob { + uint32_t dma_mode; + struct aspeed_espi_oob_dma dma; + + uint32_t rx_ready; + wait_queue_head_t wq; + + struct mutex get_rx_mtx; + struct mutex put_tx_mtx; + + spinlock_t lock; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +/* descriptor-based RX DMA handling */ +static long aspeed_espi_oob_dma_desc_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int rc = 0; + unsigned long flags; + uint32_t reg; + uint32_t wptr, sptr; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + struct oob_rx_dma_desc *d; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + regmap_read(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, ®); + wptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_WP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT; + sptr = (reg & ESPI_OOB_RX_DMA_WS_PTR_SP_MASK) >> ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT; + + d = &espi_oob->dma.rx_desc[sptr]; + + if (!d->dirty) + return -EFAULT; + + pkt_len = ((d->len) ? d->len : 0x1000) + sizeof(struct espi_comm_hdr); + + if (ioc->pkt_len < pkt_len) + return -EINVAL; + + pkt = vmalloc(pkt_len); + if (!pkt) + return -ENOMEM; + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = d->cyc; + hdr->tag = d->tag; + hdr->len_h = d->len >> 8; + hdr->len_l = d->len & 0xff; + memcpy(hdr + 1, espi_oob->dma.rx_virt + (PAGE_SIZE * sptr), pkt_len - sizeof(*hdr)); + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + spin_lock_irqsave(&espi_oob->lock, flags); + + /* make current descriptor available again */ + d->dirty = 0; + + sptr = (sptr + 1) % espi_oob->dma.rx_desc_num; + wptr = (wptr + 1) % espi_oob->dma.rx_desc_num; + + reg = ((wptr << ESPI_OOB_RX_DMA_WS_PTR_WP_SHIFT) & ESPI_OOB_RX_DMA_WS_PTR_WP_MASK) + | ((sptr << ESPI_OOB_RX_DMA_WS_PTR_SP_SHIFT) & ESPI_OOB_RX_DMA_WS_PTR_SP_MASK) + | ESPI_OOB_RX_DMA_WS_PTR_RECV_EN; + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, reg); + + /* set ready flag base on the next RX descriptor */ + espi_oob->rx_ready = espi_oob->dma.rx_desc[sptr].dirty; + + spin_unlock_irqrestore(&espi_oob->lock, flags); + +free_n_out: + vfree(pkt); + + return rc; +} + +static long aspeed_espi_oob_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int i, rc = 0; + unsigned long flags; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + if (fp->f_flags & O_NONBLOCK) { + if (mutex_trylock(&espi_oob->get_rx_mtx)) + return -EBUSY; + + if (!espi_oob->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { + mutex_lock(&espi_oob->get_rx_mtx); + + if (!espi_oob->rx_ready) { + rc = wait_event_interruptible(espi_oob->wq, + espi_oob->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; + } + } + } + + if (espi_oob->dma_mode && espi_ctrl->version != ESPI_AST2500) { + rc = aspeed_espi_oob_dma_desc_get_rx(fp, ioc, espi_oob); + goto unlock_mtx_n_out; + } + + /* common header (i.e. cycle type, tag, and length) is taken by HW */ + regmap_read(espi_ctrl->map, ESPI_OOB_RX_CTRL, ®); + cyc = (reg & ESPI_OOB_RX_CTRL_CYC_MASK) >> ESPI_OOB_RX_CTRL_CYC_SHIFT; + tag = (reg & ESPI_OOB_RX_CTRL_TAG_MASK) >> ESPI_OOB_RX_CTRL_TAG_SHIFT; + len = (reg & ESPI_OOB_RX_CTRL_LEN_MASK) >> ESPI_OOB_RX_CTRL_LEN_SHIFT; + + /* + * calculate the length of the rest part of the + * eSPI packet to be read from HW and copied to + * user space. + */ + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + sizeof(struct espi_comm_hdr); + + if (ioc->pkt_len < pkt_len) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = cyc; + hdr->tag = tag; + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + + if (espi_oob->dma_mode) { + memcpy(hdr + 1, espi_oob->dma.rx_virt, + pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) { + regmap_read(espi_ctrl->map, + ESPI_OOB_RX_PORT, ®); + pkt[i] = reg & 0xff; + } + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + spin_lock_irqsave(&espi_oob->lock, flags); + + regmap_write_bits(espi_ctrl->map, ESPI_OOB_RX_CTRL, + ESPI_OOB_RX_CTRL_PEND_SERV, + ESPI_OOB_RX_CTRL_PEND_SERV); + + espi_oob->rx_ready = 0; + + spin_unlock_irqrestore(&espi_oob->lock, flags); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_oob->get_rx_mtx); + + return rc; +} + +/* descriptor-based TX DMA handling */ +static long aspeed_espi_oob_dma_desc_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int rc = 0; + uint32_t rptr, wptr; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct oob_tx_dma_desc *d; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + pkt = vzalloc(ioc->pkt_len); + if (!pkt) + return -ENOMEM; + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* kick HW to reflect the up-to-date read/write pointer */ + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_RD_PTR, + ESPI_OOB_TX_DMA_RD_PTR_UPDATE); + + regmap_read(espi_ctrl->map, ESPI_OOB_TX_DMA_RD_PTR, &rptr); + regmap_read(espi_ctrl->map, ESPI_OOB_TX_DMA_WR_PTR, &wptr); + + if (((wptr + 1) % espi_oob->dma.tx_desc_num) == rptr) + return -EBUSY; + + d = &espi_oob->dma.tx_desc[wptr]; + d->cyc = hdr->cyc; + d->tag = hdr->tag; + d->len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + d->msg_type = OOB_DMA_TX_DESC_CUST; + + memcpy(espi_oob->dma.tx_virt + (PAGE_SIZE * wptr), hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + + dma_wmb(); + + wptr = (wptr + 1) % espi_oob->dma.tx_desc_num; + wptr |= ESPI_OOB_TX_DMA_WR_PTR_SEND_EN; + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_WR_PTR, wptr); + +free_n_out: + vfree(pkt); + + return rc; +} + +static long aspeed_espi_oob_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_oob *espi_oob) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + if (!mutex_trylock(&espi_oob->put_tx_mtx)) + return -EBUSY; + + if (espi_oob->dma_mode && espi_ctrl->version != ESPI_AST2500) { + rc = aspeed_espi_oob_dma_desc_put_tx(fp, ioc, espi_oob); + goto unlock_mtx_n_out; + } + + regmap_read(espi_ctrl->map, ESPI_OOB_TX_CTRL, ®); + if (reg & ESPI_OOB_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_mtx_n_out; + } + + if (ioc->pkt_len > ESPI_PKT_LEN_MAX) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_oob->dma_mode) { + memcpy(espi_oob->dma.tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_OOB_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_OOB_TX_CTRL_CYC_SHIFT) & ESPI_OOB_TX_CTRL_CYC_MASK) + | ((tag << ESPI_OOB_TX_CTRL_TAG_SHIFT) & ESPI_OOB_TX_CTRL_TAG_MASK) + | ((len << ESPI_OOB_TX_CTRL_LEN_SHIFT) & ESPI_OOB_TX_CTRL_LEN_MASK) + | ESPI_OOB_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_OOB_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_oob->put_tx_mtx); + + return rc; +} + +static long aspeed_espi_oob_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct aspeed_espi_ioc ioc; + struct aspeed_espi_oob *espi_oob = container_of( + fp->private_data, + struct aspeed_espi_oob, + mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; + + if (ioc.pkt_len > ESPI_PKT_LEN_MAX) + return -EINVAL; + + switch (cmd) { + case ASPEED_ESPI_OOB_GET_RX: + return aspeed_espi_oob_get_rx(fp, &ioc, espi_oob); + case ASPEED_ESPI_OOB_PUT_TX: + return aspeed_espi_oob_put_tx(fp, &ioc, espi_oob); + }; + + return -EINVAL; +} + +static const struct file_operations aspeed_espi_oob_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aspeed_espi_oob_ioctl, +}; + +static void aspeed_espi_oob_event(uint32_t sts, struct aspeed_espi_oob *espi_oob) +{ + unsigned long flags; + + if (sts & ESPI_INT_STS_OOB_RX_CMPLT) { + spin_lock_irqsave(&espi_oob->lock, flags); + espi_oob->rx_ready = 1; + spin_unlock_irqrestore(&espi_oob->lock, flags); + + wake_up_interruptible(&espi_oob->wq); + } +} + +static void aspeed_espi_oob_enable(struct aspeed_espi_oob *espi_oob) +{ + int i; + struct aspeed_espi_oob_dma *dma = &espi_oob->dma; + struct aspeed_espi_ctrl *espi_ctrl = espi_oob->ctrl; + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_SW_RDY | ESPI_CTRL_OOB_RX_SW_RST, 0); + + if (espi_oob->dma_mode) + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN, 0); + else + regmap_write(espi_ctrl->map, ESPI_OOB_RX_CTRL, ESPI_OOB_RX_CTRL_PEND_SERV); + + /* + * cleanup OOB RX FIFO to get rid of the data + * of OOB early init side-effect + */ + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_RX_SW_RST, ESPI_CTRL_OOB_RX_SW_RST); + + regmap_write(espi_ctrl->map, ESPI_OOB_RX_CTRL, + ESPI_OOB_RX_CTRL_PEND_SERV); + + if (espi_oob->dma_mode) { + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN, + ESPI_CTRL_OOB_TX_DMA_EN | ESPI_CTRL_OOB_RX_DMA_EN); + + if (espi_ctrl->version == ESPI_AST2500) { + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA, dma->tx_addr); + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA, dma->rx_addr); + } else { + for (i = 0; i < dma->tx_desc_num; ++i) + dma->tx_desc[i].data_addr = dma->tx_addr + (i * PAGE_SIZE); + + for (i = 0; i < dma->rx_desc_num; ++i) { + dma->rx_desc[i].data_addr = dma->rx_addr + (i * PAGE_SIZE); + dma->rx_desc[i].dirty = 0; + } + + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA, dma->tx_desc_addr); + regmap_write(espi_ctrl->map, ESPI_OOB_TX_DMA_RB_SIZE, dma->tx_desc_num); + + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA, dma->rx_desc_addr); + regmap_write(espi_ctrl->map, ESPI_OOB_RX_DMA_RB_SIZE, dma->rx_desc_num); + regmap_update_bits(espi_ctrl->map, ESPI_OOB_RX_DMA_WS_PTR, + ESPI_OOB_RX_DMA_WS_PTR_RECV_EN, + ESPI_OOB_RX_DMA_WS_PTR_RECV_EN); + } + } + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_OOB_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_OOB_BITS, + ESPI_INT_EN_OOB_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_OOB_SW_RDY, + ESPI_CTRL_OOB_SW_RDY); +} + +static void *aspeed_espi_oob_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc = 0; + struct aspeed_espi_oob *espi_oob; + struct aspeed_espi_oob_dma *dma; + + espi_oob = devm_kzalloc(dev, sizeof(*espi_oob), GFP_KERNEL); + if (!espi_oob) + return ERR_PTR(-ENOMEM); + + espi_oob->ctrl = espi_ctrl; + + init_waitqueue_head(&espi_oob->wq); + + spin_lock_init(&espi_oob->lock); + + mutex_init(&espi_oob->put_tx_mtx); + mutex_init(&espi_oob->get_rx_mtx); + + if (of_property_read_bool(dev->of_node, "oob,dma-mode")) + espi_oob->dma_mode = 1; + + if (espi_oob->dma_mode) { + dma = &espi_oob->dma; + + /* Descriptor based OOB DMA is supported since AST2600 */ + if (espi_ctrl->version != ESPI_AST2500) { + of_property_read_u32(dev->of_node, "oob,dma-tx-desc-num", + &dma->tx_desc_num); + of_property_read_u32(dev->of_node, "oob,dma-rx-desc-num", + &dma->rx_desc_num); + + if (!dma->tx_desc_num || !dma->rx_desc_num) { + dev_err(dev, "invalid zero number of DMA channels\n"); + return ERR_PTR(-EINVAL); + } + + if (dma->tx_desc_num >= OOB_DMA_DESC_MAX_NUM || + dma->rx_desc_num >= OOB_DMA_DESC_MAX_NUM) { + dev_err(dev, "too many number of DMA channels\n"); + return ERR_PTR(-EINVAL); + } + + dma->tx_desc = dma_alloc_coherent(dev, + sizeof(*dma->tx_desc) * dma->tx_desc_num, + &dma->tx_desc_addr, GFP_KERNEL); + if (!dma->tx_desc) { + dev_err(dev, "cannot allocate DMA TX descriptor\n"); + return ERR_PTR(-ENOMEM); + } + + dma->rx_desc = dma_alloc_coherent(dev, + sizeof(*dma->rx_desc) * dma->rx_desc_num, + &dma->rx_desc_addr, GFP_KERNEL); + if (!dma->rx_desc) { + dev_err(dev, "cannot allocate DMA RX descriptor\n"); + return ERR_PTR(-ENOMEM); + } + } + + /* + * DMA descriptors are consumed in the circular + * queue paradigm. Therefore, one dummy slot is + * reserved to detect the full condition. + * + * For AST2500 without DMA descriptors supported, + * the number of the queue slot should be 1 here. + */ + dma->tx_desc_num += 1; + dma->rx_desc_num += 1; + + dma->tx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->tx_desc_num, + &dma->tx_addr, GFP_KERNEL); + if (!dma->tx_virt) { + dev_err(dev, "cannot allocate DMA TX buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->rx_virt = dma_alloc_coherent(dev, PAGE_SIZE * dma->rx_desc_num, + &dma->rx_addr, GFP_KERNEL); + if (!dma->rx_virt) { + dev_err(dev, "cannot allocate DMA RX buffer\n"); + return ERR_PTR(-ENOMEM); + } + } + + espi_oob->mdev.parent = dev; + espi_oob->mdev.minor = MISC_DYNAMIC_MINOR; + espi_oob->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", OOB_MDEV_NAME); + espi_oob->mdev.fops = &aspeed_espi_oob_fops; + rc = misc_register(&espi_oob->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_oob_enable(espi_oob); + + return espi_oob; +} + +static void aspeed_espi_oob_free(struct device *dev, struct aspeed_espi_oob *espi_oob) +{ + struct aspeed_espi_oob_dma *dma = &espi_oob->dma; + + if (espi_oob->dma_mode) { + dma_free_coherent(dev, sizeof(*dma->tx_desc) * dma->tx_desc_num, + dma->tx_desc, dma->tx_desc_addr); + dma_free_coherent(dev, sizeof(*dma->rx_desc) * dma->rx_desc_num, + dma->rx_desc, dma->rx_desc_addr); + dma_free_coherent(dev, PAGE_SIZE * dma->tx_desc_num, + dma->tx_virt, dma->tx_addr); + dma_free_coherent(dev, PAGE_SIZE * dma->rx_desc_num, + dma->rx_virt, dma->rx_addr); + } + + mutex_destroy(&espi_oob->put_tx_mtx); + mutex_destroy(&espi_oob->get_rx_mtx); + + misc_deregister(&espi_oob->mdev); +} + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-perif.h b/drivers/soc/aspeed/aspeed-espi-perif.h new file mode 100644 index 000000000000..0bdd71ee828a --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-perif.h @@ -0,0 +1,539 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 ASPEED Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI peripheral channel. It is part of Aspeed eSPI + * slave driver implementation + */ +#ifndef _ASPEED_ESPI_PERIF_H_ +#define _ASPEED_ESPI_PERIF_H_ + +#define PERIF_MDEV_NAME "aspeed-espi-peripheral" +#define PERIF_MEMCYC_UNLOCK_KEY 0xfedc756e +#define PERIF_MEMCYC_SIZE_MIN 0x10000 + +struct aspeed_espi_perif_dma { + void *pc_tx_virt; + dma_addr_t pc_tx_addr; + void *pc_rx_virt; + dma_addr_t pc_rx_addr; + void *np_tx_virt; + dma_addr_t np_tx_addr; +}; + +struct aspeed_espi_perif { + uint32_t mcyc_enable; + void *mcyc_virt; + phys_addr_t mcyc_saddr; + phys_addr_t mcyc_taddr; + uint32_t mcyc_size; + uint32_t mcyc_mask; + + uint32_t dma_mode; + struct aspeed_espi_perif_dma dma; + + uint32_t rx_ready; + wait_queue_head_t wq; + + spinlock_t lock; + struct mutex pc_rx_mtx; + struct mutex pc_tx_mtx; + struct mutex np_tx_mtx; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +static long aspeed_espi_perif_pc_get_rx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_perif *espi_perif) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + uint32_t pkt_len; + struct espi_comm_hdr *hdr; + unsigned long flags; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (fp->f_flags & O_NONBLOCK) { + if (mutex_trylock(&espi_perif->pc_rx_mtx)) + return -EBUSY; + + if (!espi_perif->rx_ready) { + rc = -ENODATA; + goto unlock_mtx_n_out; + } + } else { + mutex_lock(&espi_perif->pc_rx_mtx); + + if (!espi_perif->rx_ready) { + rc = wait_event_interruptible(espi_perif->wq, + espi_perif->rx_ready); + if (rc == -ERESTARTSYS) { + rc = -EINTR; + goto unlock_mtx_n_out; + } + } + } + + /* common header (i.e. cycle type, tag, and length) is taken by HW */ + regmap_read(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL, ®); + cyc = (reg & ESPI_PERIF_PC_RX_CTRL_CYC_MASK) >> ESPI_PERIF_PC_RX_CTRL_CYC_SHIFT; + tag = (reg & ESPI_PERIF_PC_RX_CTRL_TAG_MASK) >> ESPI_PERIF_PC_RX_CTRL_TAG_SHIFT; + len = (reg & ESPI_PERIF_PC_RX_CTRL_LEN_MASK) >> ESPI_PERIF_PC_RX_CTRL_LEN_SHIFT; + + /* + * calculate the length of the rest part of the + * eSPI packet to be read from HW and copied to + * user space. + */ + switch (cyc) { + case ESPI_PERIF_MSG: + pkt_len = len + sizeof(struct espi_perif_msg); + break; + case ESPI_PERIF_MSG_D: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_perif_msg); + break; + case ESPI_PERIF_SUC_CMPLT_D_MIDDLE: + case ESPI_PERIF_SUC_CMPLT_D_FIRST: + case ESPI_PERIF_SUC_CMPLT_D_LAST: + case ESPI_PERIF_SUC_CMPLT_D_ONLY: + pkt_len = ((len) ? len : ESPI_PLD_LEN_MAX) + + sizeof(struct espi_perif_cmplt); + break; + case ESPI_PERIF_SUC_CMPLT: + case ESPI_PERIF_UNSUC_CMPLT: + pkt_len = len + sizeof(struct espi_perif_cmplt); + break; + default: + rc = -EFAULT; + goto unlock_mtx_n_out; + } + + if (ioc->pkt_len < pkt_len) { + rc = -EINVAL; + goto unlock_mtx_n_out; + } + + pkt = vmalloc(pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_mtx_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + hdr->cyc = cyc; + hdr->tag = tag; + hdr->len_h = len >> 8; + hdr->len_l = len & 0xff; + + if (espi_perif->dma_mode) { + memcpy(hdr + 1, espi_perif->dma.pc_rx_virt, + pkt_len - sizeof(*hdr)); + } else { + for (i = sizeof(*hdr); i < pkt_len; ++i) { + regmap_read(espi_ctrl->map, + ESPI_PERIF_PC_RX_PORT, ®); + pkt[i] = reg & 0xff; + } + } + + if (copy_to_user((void __user *)ioc->pkt, pkt, pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + spin_lock_irqsave(&espi_perif->lock, flags); + + regmap_write_bits(espi_ctrl->map, ESPI_PERIF_PC_RX_CTRL, + ESPI_PERIF_PC_RX_CTRL_PEND_SERV, + ESPI_PERIF_PC_RX_CTRL_PEND_SERV); + + espi_perif->rx_ready = 0; + + spin_unlock_irqrestore(&espi_perif->lock, flags); + +free_n_out: + vfree(pkt); + +unlock_mtx_n_out: + mutex_unlock(&espi_perif->pc_rx_mtx); + + return rc; +} + +static long aspeed_espi_perif_pc_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_perif *espi_perif) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (!mutex_trylock(&espi_perif->pc_tx_mtx)) + return -EAGAIN; + + regmap_read(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, ®); + if (reg & ESPI_PERIF_PC_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_perif->dma_mode) { + memcpy(espi_perif->dma.pc_tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_PERIF_PC_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_PERIF_PC_TX_CTRL_CYC_SHIFT) & ESPI_PERIF_PC_TX_CTRL_CYC_MASK) + | ((tag << ESPI_PERIF_PC_TX_CTRL_TAG_SHIFT) & ESPI_PERIF_PC_TX_CTRL_TAG_MASK) + | ((len << ESPI_PERIF_PC_TX_CTRL_LEN_SHIFT) & ESPI_PERIF_PC_TX_CTRL_LEN_MASK) + | ESPI_PERIF_PC_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_n_out: + mutex_unlock(&espi_perif->pc_tx_mtx); + + return rc; +} + +static long aspeed_espi_perif_np_put_tx(struct file *fp, + struct aspeed_espi_ioc *ioc, + struct aspeed_espi_perif *espi_perif) +{ + int i, rc = 0; + uint32_t reg; + uint32_t cyc, tag, len; + uint8_t *pkt; + struct espi_comm_hdr *hdr; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (!mutex_trylock(&espi_perif->np_tx_mtx)) + return -EAGAIN; + + regmap_read(espi_ctrl->map, ESPI_PERIF_NP_TX_CTRL, ®); + if (reg & ESPI_PERIF_NP_TX_CTRL_TRIGGER) { + rc = -EBUSY; + goto unlock_n_out; + } + + pkt = vmalloc(ioc->pkt_len); + if (!pkt) { + rc = -ENOMEM; + goto unlock_n_out; + } + + hdr = (struct espi_comm_hdr *)pkt; + + if (copy_from_user(pkt, (void __user *)ioc->pkt, ioc->pkt_len)) { + rc = -EFAULT; + goto free_n_out; + } + + /* + * common header (i.e. cycle type, tag, and length) + * part is written to HW registers + */ + if (espi_perif->dma_mode) { + memcpy(espi_perif->dma.np_tx_virt, hdr + 1, + ioc->pkt_len - sizeof(*hdr)); + dma_wmb(); + } else { + for (i = sizeof(*hdr); i < ioc->pkt_len; ++i) + regmap_write(espi_ctrl->map, + ESPI_PERIF_NP_TX_PORT, pkt[i]); + } + + cyc = hdr->cyc; + tag = hdr->tag; + len = (hdr->len_h << 8) | (hdr->len_l & 0xff); + + reg = ((cyc << ESPI_PERIF_NP_TX_CTRL_CYC_SHIFT) & ESPI_PERIF_NP_TX_CTRL_CYC_MASK) + | ((tag << ESPI_PERIF_NP_TX_CTRL_TAG_SHIFT) & ESPI_PERIF_NP_TX_CTRL_TAG_MASK) + | ((len << ESPI_PERIF_NP_TX_CTRL_LEN_SHIFT) & ESPI_PERIF_NP_TX_CTRL_LEN_MASK) + | ESPI_PERIF_NP_TX_CTRL_TRIGGER; + + regmap_write(espi_ctrl->map, ESPI_PERIF_NP_TX_CTRL, reg); + +free_n_out: + vfree(pkt); + +unlock_n_out: + mutex_unlock(&espi_perif->np_tx_mtx); + + return rc; + +} + +static long aspeed_espi_perif_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + struct aspeed_espi_ioc ioc; + struct aspeed_espi_perif *espi_perif = container_of( + fp->private_data, + struct aspeed_espi_perif, + mdev); + + if (copy_from_user(&ioc, (void __user *)arg, sizeof(ioc))) + return -EFAULT; + + if (ioc.pkt_len > ESPI_PKT_LEN_MAX) + return -EINVAL; + + switch (cmd) { + case ASPEED_ESPI_PERIF_PC_GET_RX: + return aspeed_espi_perif_pc_get_rx(fp, &ioc, espi_perif); + case ASPEED_ESPI_PERIF_PC_PUT_TX: + return aspeed_espi_perif_pc_put_tx(fp, &ioc, espi_perif); + case ASPEED_ESPI_PERIF_NP_PUT_TX: + return aspeed_espi_perif_np_put_tx(fp, &ioc, espi_perif); + }; + + return -EINVAL; +} + +static int aspeed_espi_perif_mmap(struct file *fp, struct vm_area_struct *vma) +{ + struct aspeed_espi_perif *espi_perif = container_of( + fp->private_data, + struct aspeed_espi_perif, + mdev); + unsigned long vm_size = vma->vm_end - vma->vm_start; + pgprot_t prot = vma->vm_page_prot; + + if (!espi_perif->mcyc_enable) + return -EPERM; + + if (((vma->vm_pgoff << PAGE_SHIFT) + vm_size) > espi_perif->mcyc_size) + return -EINVAL; + + prot = pgprot_noncached(prot); + + if (remap_pfn_range(vma, vma->vm_start, + (espi_perif->mcyc_taddr >> PAGE_SHIFT) + vma->vm_pgoff, + vm_size, prot)) + return -EAGAIN; + + return 0; +} + +static const struct file_operations aspeed_espi_perif_fops = { + .owner = THIS_MODULE, + .mmap = aspeed_espi_perif_mmap, + .unlocked_ioctl = aspeed_espi_perif_ioctl, +}; + +static void aspeed_espi_perif_event(uint32_t sts, struct aspeed_espi_perif *espi_perif) +{ + unsigned long flags; + + if (sts & ESPI_INT_STS_PERIF_PC_RX_CMPLT) { + spin_lock_irqsave(&espi_perif->lock, flags); + espi_perif->rx_ready = 1; + spin_unlock_irqrestore(&espi_perif->lock, flags); + + wake_up_interruptible(&espi_perif->wq); + } +} + +static void aspeed_espi_perif_enable(struct aspeed_espi_perif *espi_perif) +{ + struct aspeed_espi_perif_dma *dma = &espi_perif->dma; + struct aspeed_espi_ctrl *espi_ctrl = espi_perif->ctrl; + + if (espi_perif->mcyc_enable) { + if (espi_ctrl->version == ESPI_AST2500) { + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK, + PERIF_MEMCYC_UNLOCK_KEY); + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK, + espi_perif->mcyc_mask); + } else { + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_MASK, + espi_perif->mcyc_mask | ESPI_PERIF_PC_RX_MASK_CFG_WP); + regmap_update_bits(espi_ctrl->map, ESPI_CTRL2, + ESPI_CTRL2_MEMCYC_RD_DIS | ESPI_CTRL2_MEMCYC_WR_DIS, 0); + } + + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_SADDR, espi_perif->mcyc_saddr); + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_TADDR, espi_perif->mcyc_taddr); + } + + if (espi_perif->dma_mode) { + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_RX_DMA, dma->pc_rx_addr); + regmap_write(espi_ctrl->map, ESPI_PERIF_PC_TX_DMA, dma->pc_tx_addr); + regmap_write(espi_ctrl->map, ESPI_PERIF_NP_TX_DMA, dma->np_tx_addr); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_PERIF_NP_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_RX_DMA_EN, + ESPI_CTRL_PERIF_NP_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_TX_DMA_EN | + ESPI_CTRL_PERIF_PC_RX_DMA_EN); + } + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_PERIF_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_PERIF_BITS, + ESPI_INT_EN_PERIF_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_PERIF_SW_RDY, + ESPI_CTRL_PERIF_SW_RDY); +} + +static void *aspeed_espi_perif_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc; + struct aspeed_espi_perif *espi_perif; + struct aspeed_espi_perif_dma *dma; + + espi_perif = devm_kzalloc(dev, sizeof(*espi_perif), GFP_KERNEL); + if (!espi_perif) + return ERR_PTR(-ENOMEM); + + espi_perif->ctrl = espi_ctrl; + + init_waitqueue_head(&espi_perif->wq); + + spin_lock_init(&espi_perif->lock); + + mutex_init(&espi_perif->pc_rx_mtx); + mutex_init(&espi_perif->pc_tx_mtx); + mutex_init(&espi_perif->np_tx_mtx); + + espi_perif->mcyc_enable = of_property_read_bool(dev->of_node, "perif,memcyc-enable"); + if (espi_perif->mcyc_enable) { + rc = of_property_read_u32(dev->of_node, "perif,memcyc-src-addr", + &espi_perif->mcyc_saddr); + if (rc) { + dev_err(dev, "cannot get Host source address for memory cycle\n"); + return ERR_PTR(-ENODEV); + } + + rc = of_property_read_u32(dev->of_node, "perif,memcyc-size", + &espi_perif->mcyc_size); + if (rc) { + dev_err(dev, "cannot get size for memory cycle\n"); + return ERR_PTR(-ENODEV); + } + + if (espi_perif->mcyc_size < PERIF_MEMCYC_SIZE_MIN) + espi_perif->mcyc_size = PERIF_MEMCYC_SIZE_MIN; + else + espi_perif->mcyc_size = roundup_pow_of_two(espi_perif->mcyc_size); + + espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1); + espi_perif->mcyc_virt = dma_alloc_coherent(dev, espi_perif->mcyc_size, + &espi_perif->mcyc_taddr, GFP_KERNEL); + if (!espi_perif->mcyc_virt) { + dev_err(dev, "cannot allocate memory cycle region\n"); + return ERR_PTR(-ENOMEM); + } + } + + if (of_property_read_bool(dev->of_node, "perif,dma-mode")) { + dma = &espi_perif->dma; + + dma->pc_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->pc_tx_addr, GFP_KERNEL); + if (!dma->pc_tx_virt) { + dev_err(dev, "cannot allocate posted TX DMA buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->pc_rx_addr, GFP_KERNEL); + if (!dma->pc_rx_virt) { + dev_err(dev, "cannot allocate posted RX DMA buffer\n"); + return ERR_PTR(-ENOMEM); + } + + dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, + &dma->np_tx_addr, GFP_KERNEL); + if (!dma->np_tx_virt) { + dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); + return ERR_PTR(-ENOMEM); + } + + espi_perif->dma_mode = 1; + } + + espi_perif->mdev.parent = dev; + espi_perif->mdev.minor = MISC_DYNAMIC_MINOR; + espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", PERIF_MDEV_NAME); + espi_perif->mdev.fops = &aspeed_espi_perif_fops; + rc = misc_register(&espi_perif->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_perif_enable(espi_perif); + + return espi_perif; +} + +static void aspeed_espi_perif_free(struct device *dev, struct aspeed_espi_perif *espi_perif) +{ + struct aspeed_espi_perif_dma *dma = &espi_perif->dma; + + if (espi_perif->mcyc_virt) + dma_free_coherent(dev, espi_perif->mcyc_size, + espi_perif->mcyc_virt, + espi_perif->mcyc_taddr); + + if (espi_perif->dma_mode) { + dma_free_coherent(dev, PAGE_SIZE, dma->pc_tx_virt, + dma->pc_tx_addr); + dma_free_coherent(dev, PAGE_SIZE, dma->pc_rx_virt, + dma->pc_rx_addr); + dma_free_coherent(dev, PAGE_SIZE, dma->np_tx_virt, + dma->np_tx_addr); + } + + mutex_destroy(&espi_perif->pc_tx_mtx); + mutex_destroy(&espi_perif->np_tx_mtx); + + misc_deregister(&espi_perif->mdev); +} + +#endif diff --git a/drivers/soc/aspeed/aspeed-espi-vw.h b/drivers/soc/aspeed/aspeed-espi-vw.h new file mode 100644 index 000000000000..3d49366c912e --- /dev/null +++ b/drivers/soc/aspeed/aspeed-espi-vw.h @@ -0,0 +1,142 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright 2021 ASPEED Technology Inc. + * + * This header includes the data strcuture and the handler + * for eSPI virtual wire channel. It is part of Aspeed eSPI + * slave driver implementation + */ +#ifndef _ASPEED_ESPI_VW_H_ +#define _ASPEED_ESPI_VW_H_ + +#define VW_MDEV_NAME "aspeed-espi-vw" + +struct aspeed_espi_vw { + int irq; + int irq_reset; + + struct miscdevice mdev; + struct aspeed_espi_ctrl *ctrl; +}; + +static long aspeed_espi_vw_ioctl(struct file *fp, unsigned int cmd, + unsigned long arg) +{ + uint32_t val; + + struct aspeed_espi_vw *espi_vw = container_of( + fp->private_data, + struct aspeed_espi_vw, + mdev); + struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl; + + switch (cmd) { + case ASPEED_ESPI_VW_GET_GPIO_VAL: + regmap_read(espi_ctrl->map, ESPI_VW_GPIO_VAL, &val); + if (put_user(val, (uint32_t __user *)arg)) + return -EFAULT; + break; + + case ASPEED_ESPI_VW_PUT_GPIO_VAL: + if (get_user(val, (uint32_t __user *)arg)) + return -EFAULT; + regmap_write(espi_ctrl->map, ESPI_VW_GPIO_VAL, val); + break; + + default: + return -EINVAL; + }; + + return 0; +} + +static const struct file_operations aspeed_espi_vw_fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = aspeed_espi_vw_ioctl, +}; + +static void aspeed_espi_vw_event(uint32_t sts, struct aspeed_espi_vw *espi_vw) +{ + uint32_t sysevt_sts; + struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl; + + regmap_read(espi_ctrl->map, ESPI_INT_STS, &sts); + + if (sts & ESPI_INT_STS_VW_SYSEVT) { + regmap_read(espi_ctrl->map, ESPI_SYSEVT_INT_STS, &sysevt_sts); + + if (espi_ctrl->version == ESPI_AST2500) { + if (sysevt_sts & ESPI_SYSEVT_INT_STS_HOST_RST_WARN) + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT, + ESPI_SYSEVT_HOST_RST_ACK, + ESPI_SYSEVT_HOST_RST_ACK); + + if (sysevt_sts & ESPI_SYSEVT_INT_STS_OOB_RST_WARN) + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT, + ESPI_SYSEVT_OOB_RST_ACK, + ESPI_SYSEVT_OOB_RST_ACK); + } + + regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_STS, sysevt_sts); + } + + if (sts & ESPI_INT_STS_VW_SYSEVT1) { + regmap_read(espi_ctrl->map, ESPI_SYSEVT1_INT_STS, &sysevt_sts); + + if (sysevt_sts & ESPI_SYSEVT1_INT_STS_SUSPEND_WARN) + regmap_update_bits(espi_ctrl->map, ESPI_SYSEVT1, + ESPI_SYSEVT1_SUSPEND_ACK, + ESPI_SYSEVT1_SUSPEND_ACK); + + regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_STS, sysevt_sts); + } +} + +static void aspeed_espi_vw_enable(struct aspeed_espi_vw *espi_vw) +{ + struct aspeed_espi_ctrl *espi_ctrl = espi_vw->ctrl; + + regmap_write(espi_ctrl->map, ESPI_INT_STS, + ESPI_INT_STS_VW_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, + ESPI_INT_EN_VW_BITS, + ESPI_INT_EN_VW_BITS); + + regmap_update_bits(espi_ctrl->map, ESPI_CTRL, + ESPI_CTRL_VW_SW_RDY, + ESPI_CTRL_VW_SW_RDY); +} + +static void *aspeed_espi_vw_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) +{ + int rc; + struct aspeed_espi_vw *espi_vw; + + espi_vw = devm_kzalloc(dev, sizeof(*espi_vw), GFP_KERNEL); + if (!espi_vw) + return ERR_PTR(-ENOMEM); + + espi_vw->ctrl = espi_ctrl; + + espi_vw->mdev.parent = dev; + espi_vw->mdev.minor = MISC_DYNAMIC_MINOR; + espi_vw->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", VW_MDEV_NAME); + espi_vw->mdev.fops = &aspeed_espi_vw_fops; + rc = misc_register(&espi_vw->mdev); + if (rc) { + dev_err(dev, "cannot register device\n"); + return ERR_PTR(rc); + } + + aspeed_espi_vw_enable(espi_vw); + + return espi_vw; +} + +static void aspeed_espi_vw_free(struct device *dev, struct aspeed_espi_vw *espi_vw) +{ + misc_deregister(&espi_vw->mdev); +} + +#endif -- 2.17.1 From robh at kernel.org Thu Aug 26 23:26:01 2021 From: robh at kernel.org (Rob Herring) Date: Thu, 26 Aug 2021 08:26:01 -0500 Subject: [PATCH v3 1/4] dt-bindings: aspeed: Add eSPI controller In-Reply-To: <20210826061623.6352-2-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> <20210826061623.6352-2-chiawei_wang@aspeedtech.com> Message-ID: <1629984361.334223.1033382.nullmailer@robh.at.kernel.org> On Thu, 26 Aug 2021 14:16:20 +0800, Chia-Wei Wang wrote: > Add dt-bindings for Aspeed eSPI controller > > Signed-off-by: Chia-Wei Wang > --- > .../devicetree/bindings/soc/aspeed/espi.yaml | 157 ++++++++++++++++++ > 1 file changed, 157 insertions(+) > create mode 100644 Documentation/devicetree/bindings/soc/aspeed/espi.yaml > My bot found errors running 'make DT_CHECKER_FLAGS=-m dt_binding_check' on your patch (DT_CHECKER_FLAGS is new in v5.13): yamllint warnings/errors: dtschema/dtc warnings/errors: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/aspeed/espi.example.dt.yaml: espi at 1e6ee000: 'espi-ctrl at 0', 'espi-mmbi at 800' do not match any of the regexes: 'pinctrl-[0-9]+' From schema: /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/aspeed/espi.yaml doc reference errors (make refcheckdocs): See https://patchwork.ozlabs.org/patch/1520968 This check can fail if there are any dependencies. The base for a patch series is generally the most recent rc1. If you already ran 'make dt_binding_check' and didn't see the above error(s), then make sure 'yamllint' is installed and dt-schema is up to date: pip3 install dtschema --upgrade Please check and re-submit. From iwona.winiarska at intel.com Fri Aug 27 08:40:43 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Thu, 26 Aug 2021 22:40:43 +0000 Subject: [PATCH v2 06/15] peci: Add core infrastructure In-Reply-To: References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-7-iwona.winiarska@intel.com> Message-ID: <4aa3fac69a92b175beb59d2678eb914c35ea98b7.camel@intel.com> On Wed, 2021-08-25 at 15:58 -0700, Dan Williams wrote: > On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska > wrote: > > > > Intel processors provide access for various services designed to support > > processor and DRAM thermal management, platform manageability and > > processor interface tuning and diagnostics. > > Those services are available via the Platform Environment Control > > Interface (PECI) that provides a communication channel between the > > processor and the Baseboard Management Controller (BMC) or other > > platform management device. > > > > This change introduces PECI subsystem by adding the initial core module > > and API for controller drivers. > > > > Co-developed-by: Jason M Bills > > Signed-off-by: Jason M Bills > > Co-developed-by: Jae Hyun Yoo > > Signed-off-by: Jae Hyun Yoo > > Signed-off-by: Iwona Winiarska > > Reviewed-by: Pierre-Louis Bossart > > --- > > ?MAINTAINERS???????????? |?? 9 +++ > > ?drivers/Kconfig???????? |?? 3 + > > ?drivers/Makefile??????? |?? 1 + > > ?drivers/peci/Kconfig??? |? 15 ++++ > > ?drivers/peci/Makefile?? |?? 5 ++ > > ?drivers/peci/core.c???? | 155 ++++++++++++++++++++++++++++++++++++++++ > > ?drivers/peci/internal.h |? 16 +++++ > > ?include/linux/peci.h??? |? 99 +++++++++++++++++++++++++ > > ?8 files changed, 303 insertions(+) > > ?create mode 100644 drivers/peci/Kconfig > > ?create mode 100644 drivers/peci/Makefile > > ?create mode 100644 drivers/peci/core.c > > ?create mode 100644 drivers/peci/internal.h > > ?create mode 100644 include/linux/peci.h > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index 7cdab7229651..d411974aaa5e 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -14503,6 +14503,15 @@ L:???? platform-driver-x86 at vger.kernel.org > > ?S:???? Maintained > > ?F:???? drivers/platform/x86/peaq-wmi.c > > > > +PECI SUBSYSTEM > > +M:???? Iwona Winiarska > > +R:???? Jae Hyun Yoo > > +L:???? openbmc at lists.ozlabs.org?(moderated for non-subscribers) > > +S:???? Supported > > +F:???? Documentation/devicetree/bindings/peci/ > > +F:???? drivers/peci/ > > +F:???? include/linux/peci.h > > + > > ?PENSANDO ETHERNET DRIVERS > > ?M:???? Shannon Nelson > > ?M:???? drivers at pensando.io > > diff --git a/drivers/Kconfig b/drivers/Kconfig > > index 8bad63417a50..f472b3d972b3 100644 > > --- a/drivers/Kconfig > > +++ b/drivers/Kconfig > > @@ -236,4 +236,7 @@ source "drivers/interconnect/Kconfig" > > ?source "drivers/counter/Kconfig" > > > > ?source "drivers/most/Kconfig" > > + > > +source "drivers/peci/Kconfig" > > + > > ?endmenu > > diff --git a/drivers/Makefile b/drivers/Makefile > > index 27c018bdf4de..8d96f0c3dde5 100644 > > --- a/drivers/Makefile > > +++ b/drivers/Makefile > > @@ -189,3 +189,4 @@ obj-$(CONFIG_GNSS)????????? += gnss/ > > ?obj-$(CONFIG_INTERCONNECT)???? += interconnect/ > > ?obj-$(CONFIG_COUNTER)????????? += counter/ > > ?obj-$(CONFIG_MOST)???????????? += most/ > > +obj-$(CONFIG_PECI)???????????? += peci/ > > diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig > > new file mode 100644 > > index 000000000000..71a4ad81225a > > --- /dev/null > > +++ b/drivers/peci/Kconfig > > @@ -0,0 +1,15 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +menuconfig PECI > > +?????? tristate "PECI support" > > +?????? help > > +???????? The Platform Environment Control Interface (PECI) is an interface > > +???????? that provides a communication channel to Intel processors and > > +???????? chipset components from external monitoring or control devices. > > + > > +???????? If you are building a Baseboard Management Controller (BMC) kernel > > +???????? for Intel platform say Y here and also to the specific driver for > > +???????? your adapter(s) below. If unsure say N. > > + > > +???????? This support is also available as a module. If so, the module > > +???????? will be called peci. > > diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile > > new file mode 100644 > > index 000000000000..e789a354e842 > > --- /dev/null > > +++ b/drivers/peci/Makefile > > @@ -0,0 +1,5 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +# Core functionality > > +peci-y := core.o > > +obj-$(CONFIG_PECI) += peci.o > > diff --git a/drivers/peci/core.c b/drivers/peci/core.c > > new file mode 100644 > > index 000000000000..7b3938af0396 > > --- /dev/null > > +++ b/drivers/peci/core.c > > @@ -0,0 +1,155 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +// Copyright (c) 2018-2021 Intel Corporation > > + > > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > > This looks like overkill for only one print statement in this module, > especially when the dev_ print helpers offer more detail. Ok, I'll remove it. > > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include "internal.h" > > + > > +static DEFINE_IDA(peci_controller_ida); > > + > > +static void peci_controller_dev_release(struct device *dev) > > +{ > > +?????? struct peci_controller *controller = to_peci_controller(dev); > > + > > +?????? pm_runtime_disable(&controller->dev); > > This seems late to be disabling power management, the device is about > to be freed. Keep in mind the lifetime of the this object can be > artificially prolonged. I expect this to be done when the device is > unregistered from the bus. Makes sense. > > > + > > +?????? mutex_destroy(&controller->bus_lock); > > +?????? ida_free(&peci_controller_ida, controller->id); > > +?????? fwnode_handle_put(controller->dev.fwnode); > > Shouldn't the get / put of this handle reference be bound to specific > accesses not held for the entire lifetime of the object? At a minimum > it seems to be a reference that can taken at registration and dropped > at unregistration. I'll move it to take ref at registration and to drop it at unregistration. > > > +?????? kfree(controller); > > +} > > + > > +struct device_type peci_controller_type = { > > +?????? .release??????? = peci_controller_dev_release, > > +}; > > + > > +static struct peci_controller *peci_controller_alloc(struct device *dev, > > +??????????????????????????????????????????????????? struct > > peci_controller_ops *ops) > > +{ > > +?????? struct fwnode_handle *node = fwnode_handle_get(dev_fwnode(dev)); > > +?????? struct peci_controller *controller; > > +?????? int ret; > > + > > +?????? if (!ops->xfer) > > +?????????????? return ERR_PTR(-EINVAL); > > + > > +?????? controller = kzalloc(sizeof(*controller), GFP_KERNEL); > > +?????? if (!controller) > > +?????????????? return ERR_PTR(-ENOMEM); > > + > > +?????? ret = ida_alloc_max(&peci_controller_ida, U8_MAX, GFP_KERNEL); > > +?????? if (ret < 0) > > +?????????????? goto err; > > +?????? controller->id = ret; > > + > > +?????? controller->ops = ops; > > + > > +?????? controller->dev.parent = dev; > > +?????? controller->dev.bus = &peci_bus_type; > > +?????? controller->dev.type = &peci_controller_type; > > +?????? controller->dev.fwnode = node; > > +?????? controller->dev.of_node = to_of_node(node); > > + > > +?????? device_initialize(&controller->dev); > > + > > +?????? mutex_init(&controller->bus_lock); > > + > > +?????? pm_runtime_no_callbacks(&controller->dev); > > +?????? pm_suspend_ignore_children(&controller->dev, true); > > +?????? pm_runtime_enable(&controller->dev); > > Per above, are you sure unregistered devices need pm_runtime enabled? > > Rest looks ok to me. Thanks -Iwona From iwona.winiarska at intel.com Fri Aug 27 09:54:56 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Thu, 26 Aug 2021 23:54:56 +0000 Subject: [PATCH v2 07/15] peci: Add peci-aspeed controller driver In-Reply-To: References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-8-iwona.winiarska@intel.com> Message-ID: On Wed, 2021-08-25 at 18:35 -0700, Dan Williams wrote: > On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska > wrote: > > > > From: Jae Hyun Yoo > > > > ASPEED AST24xx/AST25xx/AST26xx SoCs supports the PECI electrical > > interface (a.k.a PECI wire). > > Maybe a one sentence blurb here and in the Kconfig reminding people > why they should care if they have a PECI driver or not? Ok, I'll expand it a bit. > > > > > Signed-off-by: Jae Hyun Yoo > > Co-developed-by: Iwona Winiarska > > Signed-off-by: Iwona Winiarska > > Reviewed-by: Pierre-Louis Bossart > > --- > > ?MAINTAINERS?????????????????????????? |?? 9 + > > ?drivers/peci/Kconfig????????????????? |?? 6 + > > ?drivers/peci/Makefile???????????????? |?? 3 + > > ?drivers/peci/controller/Kconfig?????? |? 16 + > > ?drivers/peci/controller/Makefile????? |?? 3 + > > ?drivers/peci/controller/peci-aspeed.c | 445 ++++++++++++++++++++++++++ > > ?6 files changed, 482 insertions(+) > > ?create mode 100644 drivers/peci/controller/Kconfig > > ?create mode 100644 drivers/peci/controller/Makefile > > ?create mode 100644 drivers/peci/controller/peci-aspeed.c > > > > diff --git a/MAINTAINERS b/MAINTAINERS > > index d411974aaa5e..6e9d53ff68ab 100644 > > --- a/MAINTAINERS > > +++ b/MAINTAINERS > > @@ -2866,6 +2866,15 @@ S:?????? Maintained > > ?F:???? Documentation/hwmon/asc7621.rst > > ?F:???? drivers/hwmon/asc7621.c > > > > +ASPEED PECI CONTROLLER > > +M:???? Iwona Winiarska > > +M:???? Jae Hyun Yoo > > +L:???? linux-aspeed at lists.ozlabs.org?(moderated for non-subscribers) > > +L:???? openbmc at lists.ozlabs.org?(moderated for non-subscribers) > > +S:???? Supported > > +F:???? Documentation/devicetree/bindings/peci/peci-aspeed.yaml > > +F:???? drivers/peci/controller/peci-aspeed.c > > + > > ?ASPEED PINCTRL DRIVERS > > ?M:???? Andrew Jeffery > > ?L:???? linux-aspeed at lists.ozlabs.org?(moderated for non-subscribers) > > diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig > > index 71a4ad81225a..99279df97a78 100644 > > --- a/drivers/peci/Kconfig > > +++ b/drivers/peci/Kconfig > > @@ -13,3 +13,9 @@ menuconfig PECI > > > > ????????? This support is also available as a module. If so, the module > > ????????? will be called peci. > > + > > +if PECI > > + > > +source "drivers/peci/controller/Kconfig" > > + > > +endif # PECI > > diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile > > index e789a354e842..926d8df15cbd 100644 > > --- a/drivers/peci/Makefile > > +++ b/drivers/peci/Makefile > > @@ -3,3 +3,6 @@ > > ?# Core functionality > > ?peci-y := core.o > > ?obj-$(CONFIG_PECI) += peci.o > > + > > +# Hardware specific bus drivers > > +obj-y += controller/ > > diff --git a/drivers/peci/controller/Kconfig > > b/drivers/peci/controller/Kconfig > > new file mode 100644 > > index 000000000000..6d48df08db1c > > --- /dev/null > > +++ b/drivers/peci/controller/Kconfig > > @@ -0,0 +1,16 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +config PECI_ASPEED > > +?????? tristate "ASPEED PECI support" > > +?????? depends on ARCH_ASPEED || COMPILE_TEST > > +?????? depends on OF > > +?????? depends on HAS_IOMEM > > +?????? help > > +???????? This option enables PECI controller driver for ASPEED AST2400, > > +???????? AST2500 and AST2600 SoCs. > > + > > +???????? Say Y here if your system runs on ASPEED SoC and you are using it > > +???????? as BMC for Intel platform. > > + > > +???????? This driver can also be built as a module. If so, the module will > > +???????? be called peci-aspeed. > > diff --git a/drivers/peci/controller/Makefile > > b/drivers/peci/controller/Makefile > > new file mode 100644 > > index 000000000000..022c28ef1bf0 > > --- /dev/null > > +++ b/drivers/peci/controller/Makefile > > @@ -0,0 +1,3 @@ > > +# SPDX-License-Identifier: GPL-2.0-only > > + > > +obj-$(CONFIG_PECI_ASPEED)????? += peci-aspeed.o > > diff --git a/drivers/peci/controller/peci-aspeed.c > > b/drivers/peci/controller/peci-aspeed.c > > new file mode 100644 > > index 000000000000..1d708c983749 > > --- /dev/null > > +++ b/drivers/peci/controller/peci-aspeed.c > > @@ -0,0 +1,445 @@ > > +// SPDX-License-Identifier: GPL-2.0-only > > +// Copyright (C) 2012-2017 ASPEED Technology Inc. > > +// Copyright (c) 2018-2021 Intel Corporation > > Why different copyright capitalization? I'll make them consistent. > > > + > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > +#include > > + > > +#include > > Why is this included? Leftover - I'll remove it. > > > + > > +/* ASPEED PECI Registers */ > > +/* Control Register */ > > +#define ASPEED_PECI_CTRL?????????????????????? 0x00 > > +#define?? ASPEED_PECI_CTRL_SAMPLING_MASK?????? GENMASK(19, 16) > > +#define?? ASPEED_PECI_CTRL_RD_MODE_MASK??????????????? GENMASK(13, 12) > > +#define???? ASPEED_PECI_CTRL_RD_MODE_DBG?????? BIT(13) > > +#define???? ASPEED_PECI_CTRL_RD_MODE_COUNT???? BIT(12) > > +#define?? ASPEED_PECI_CTRL_CLK_SOURCE????????? BIT(11) > > +#define?? ASPEED_PECI_CTRL_CLK_DIV_MASK??????????????? GENMASK(10, 8) > > +#define?? ASPEED_PECI_CTRL_INVERT_OUT????????? BIT(7) > > +#define?? ASPEED_PECI_CTRL_INVERT_IN?????????? BIT(6) > > +#define?? ASPEED_PECI_CTRL_BUS_CONTENTION_EN?? BIT(5) > > +#define?? ASPEED_PECI_CTRL_PECI_EN???????????? BIT(4) > > +#define?? ASPEED_PECI_CTRL_PECI_CLK_EN???????? BIT(0) > > + > > +/* Timing Negotiation Register */ > > +#define ASPEED_PECI_TIMING_NEGOTIATION???????? 0x04 > > +#define?? ASPEED_PECI_T_NEGO_MSG_MASK????????? GENMASK(15, 8) > > +#define?? ASPEED_PECI_T_NEGO_ADDR_MASK???????? GENMASK(7, 0) > > + > > +/* Command Register */ > > +#define ASPEED_PECI_CMD??????????????????????????????? 0x08 > > +#define?? ASPEED_PECI_CMD_PIN_MONITORING?????? BIT(31) > > +#define?? ASPEED_PECI_CMD_STS_MASK???????????? GENMASK(27, 24) > > +#define???? ASPEED_PECI_CMD_STS_ADDR_T_NEGO??? 0x3 > > +#define?? ASPEED_PECI_CMD_IDLE_MASK??????????? \ > > +???????? (ASPEED_PECI_CMD_STS_MASK | ASPEED_PECI_CMD_PIN_MONITORING) > > +#define?? ASPEED_PECI_CMD_FIRE???????????????? BIT(0) > > + > > +/* Read/Write Length Register */ > > +#define ASPEED_PECI_RW_LENGTH????????????????? 0x0c > > +#define?? ASPEED_PECI_AW_FCS_EN??????????????????????? BIT(31) > > +#define?? ASPEED_PECI_RD_LEN_MASK????????????? GENMASK(23, 16) > > +#define?? ASPEED_PECI_WR_LEN_MASK????????????? GENMASK(15, 8) > > +#define?? ASPEED_PECI_TARGET_ADDR_MASK???????? GENMASK(7, 0) > > + > > +/* Expected FCS Data Register */ > > +#define ASPEED_PECI_EXPECTED_FCS?????????????? 0x10 > > +#define?? ASPEED_PECI_EXPECTED_RD_FCS_MASK???? GENMASK(23, 16) > > +#define?? ASPEED_PECI_EXPECTED_AW_FCS_AUTO_MASK??????? GENMASK(15, 8) > > +#define?? ASPEED_PECI_EXPECTED_WR_FCS_MASK???? GENMASK(7, 0) > > + > > +/* Captured FCS Data Register */ > > +#define ASPEED_PECI_CAPTURED_FCS?????????????? 0x14 > > +#define?? ASPEED_PECI_CAPTURED_RD_FCS_MASK???? GENMASK(23, 16) > > +#define?? ASPEED_PECI_CAPTURED_WR_FCS_MASK???? GENMASK(7, 0) > > + > > +/* Interrupt Register */ > > +#define ASPEED_PECI_INT_CTRL?????????????????? 0x18 > > +#define?? ASPEED_PECI_TIMING_NEGO_SEL_MASK???? GENMASK(31, 30) > > +#define???? ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO?? 0 > > +#define???? ASPEED_PECI_2ND_BIT_OF_ADDR_NEGO?? 1 > > +#define???? ASPEED_PECI_MESSAGE_NEGO?????????? 2 > > +#define?? ASPEED_PECI_INT_MASK???????????????? GENMASK(4, 0) > > +#define???? ASPEED_PECI_INT_BUS_TIMEOUT??????????????? BIT(4) > > +#define???? ASPEED_PECI_INT_BUS_CONTENTION???? BIT(3) > > +#define???? ASPEED_PECI_INT_WR_FCS_BAD???????? BIT(2) > > +#define???? ASPEED_PECI_INT_WR_FCS_ABORT?????? BIT(1) > > +#define???? ASPEED_PECI_INT_CMD_DONE?????????? BIT(0) > > + > > +/* Interrupt Status Register */ > > +#define ASPEED_PECI_INT_STS??????????????????? 0x1c > > +#define?? ASPEED_PECI_INT_TIMING_RESULT_MASK?? GENMASK(29, 16) > > +???????? /* bits[4..0]: Same bit fields in the 'Interrupt Register' */ > > + > > +/* Rx/Tx Data Buffer Registers */ > > +#define ASPEED_PECI_WR_DATA0?????????????????? 0x20 > > +#define ASPEED_PECI_WR_DATA1?????????????????? 0x24 > > +#define ASPEED_PECI_WR_DATA2?????????????????? 0x28 > > +#define ASPEED_PECI_WR_DATA3?????????????????? 0x2c > > +#define ASPEED_PECI_RD_DATA0?????????????????? 0x30 > > +#define ASPEED_PECI_RD_DATA1?????????????????? 0x34 > > +#define ASPEED_PECI_RD_DATA2?????????????????? 0x38 > > +#define ASPEED_PECI_RD_DATA3?????????????????? 0x3c > > +#define ASPEED_PECI_WR_DATA4?????????????????? 0x40 > > +#define ASPEED_PECI_WR_DATA5?????????????????? 0x44 > > +#define ASPEED_PECI_WR_DATA6?????????????????? 0x48 > > +#define ASPEED_PECI_WR_DATA7?????????????????? 0x4c > > +#define ASPEED_PECI_RD_DATA4?????????????????? 0x50 > > +#define ASPEED_PECI_RD_DATA5?????????????????? 0x54 > > +#define ASPEED_PECI_RD_DATA6?????????????????? 0x58 > > +#define ASPEED_PECI_RD_DATA7?????????????????? 0x5c > > +#define?? ASPEED_PECI_DATA_BUF_SIZE_MAX??????????????? 32 > > + > > +/* Timing Negotiation */ > > +#define ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT? 8 > > +#define ASPEED_PECI_RD_SAMPLING_POINT_MAX????? (BIT(4) - 1) > > +#define ASPEED_PECI_CLK_DIV_DEFAULT??????????? 0 > > +#define ASPEED_PECI_CLK_DIV_MAX??????????????????????? (BIT(3) - 1) > > +#define ASPEED_PECI_MSG_TIMING_DEFAULT???????? 1 > > +#define ASPEED_PECI_MSG_TIMING_MAX???????????? (BIT(8) - 1) > > +#define ASPEED_PECI_ADDR_TIMING_DEFAULT??????????????? 1 > > +#define ASPEED_PECI_ADDR_TIMING_MAX??????????? (BIT(8) - 1) > > + > > +/* Timeout */ > > +#define ASPEED_PECI_IDLE_CHECK_TIMEOUT_US????? (50 * USEC_PER_MSEC) > > +#define ASPEED_PECI_IDLE_CHECK_INTERVAL_US???? (10 * USEC_PER_MSEC) > > +#define ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT???? (1000) > > +#define ASPEED_PECI_CMD_TIMEOUT_MS_MAX???????? (1000) > > + > > +struct aspeed_peci { > > +?????? struct peci_controller *controller; > > +?????? struct device *dev; > > +?????? void __iomem *base; > > +?????? struct clk *clk; > > +?????? struct reset_control *rst; > > +?????? int irq; > > +?????? spinlock_t lock; /* to sync completion status handling */ > > +?????? struct completion xfer_complete; > > +?????? u32 status; > > +?????? u32 cmd_timeout_ms; > > +?????? u32 msg_timing; > > +?????? u32 addr_timing; > > +?????? u32 rd_sampling_point; > > +?????? u32 clk_div; > > +}; > > + > > +static void aspeed_peci_init_regs(struct aspeed_peci *priv) > > +{ > > +?????? u32 val; > > + > > +?????? val = FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, > > ASPEED_PECI_CLK_DIV_DEFAULT); > > +?????? val |= ASPEED_PECI_CTRL_PECI_CLK_EN; > > +?????? writel(val, priv->base + ASPEED_PECI_CTRL); > > +?????? /* > > +??????? * Timing negotiation period setting. > > +??????? * The unit of the programmed value is 4 times of PECI clock period. > > +??????? */ > > +?????? val = FIELD_PREP(ASPEED_PECI_T_NEGO_MSG_MASK, priv->msg_timing); > > +?????? val |= FIELD_PREP(ASPEED_PECI_T_NEGO_ADDR_MASK, priv->addr_timing); > > +?????? writel(val, priv->base + ASPEED_PECI_TIMING_NEGOTIATION); > > + > > +?????? /* Clear interrupts */ > > +?????? val = readl(priv->base + ASPEED_PECI_INT_STS) | > > ASPEED_PECI_INT_MASK; > > +?????? writel(val, priv->base + ASPEED_PECI_INT_STS); > > + > > +?????? /* Set timing negotiation mode and enable interrupts */ > > +?????? val = FIELD_PREP(ASPEED_PECI_TIMING_NEGO_SEL_MASK, > > ASPEED_PECI_1ST_BIT_OF_ADDR_NEGO); > > +?????? val |= ASPEED_PECI_INT_MASK; > > +?????? writel(val, priv->base + ASPEED_PECI_INT_CTRL); > > + > > +?????? val = FIELD_PREP(ASPEED_PECI_CTRL_SAMPLING_MASK, priv- > > >rd_sampling_point); > > +?????? val |= FIELD_PREP(ASPEED_PECI_CTRL_CLK_DIV_MASK, priv->clk_div); > > +?????? val |= ASPEED_PECI_CTRL_PECI_EN; > > +?????? val |= ASPEED_PECI_CTRL_PECI_CLK_EN; > > +?????? writel(val, priv->base + ASPEED_PECI_CTRL); > > +} > > + > > +static inline int aspeed_peci_check_idle(struct aspeed_peci *priv) > > +{ > > +?????? u32 cmd_sts = readl(priv->base + ASPEED_PECI_CMD); > > + > > +?????? if (FIELD_GET(ASPEED_PECI_CMD_STS_MASK, cmd_sts) == > > ASPEED_PECI_CMD_STS_ADDR_T_NEGO) > > +?????????????? aspeed_peci_init_regs(priv); > > + > > +?????? return readl_poll_timeout(priv->base + ASPEED_PECI_CMD, > > +???????????????????????????????? cmd_sts, > > +???????????????????????????????? !(cmd_sts & ASPEED_PECI_CMD_IDLE_MASK), > > +???????????????????????????????? ASPEED_PECI_IDLE_CHECK_INTERVAL_US, > > +???????????????????????????????? ASPEED_PECI_IDLE_CHECK_TIMEOUT_US); > > +} > > + > > +static int aspeed_peci_xfer(struct peci_controller *controller, > > +?????????????????????????? u8 addr, struct peci_request *req) > > +{ > > +?????? struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent); > > +?????? unsigned long flags, timeout = msecs_to_jiffies(priv- > > >cmd_timeout_ms); > > +?????? u32 peci_head; > > +?????? int ret; > > + > > +?????? if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX || > > +?????????? req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX) > > +?????????????? return -EINVAL; > > + > > +?????? /* Check command sts and bus idle state */ > > +?????? ret = aspeed_peci_check_idle(priv); > > +?????? if (ret) > > +?????????????? return ret; /* -ETIMEDOUT */ > > + > > +?????? spin_lock_irqsave(&priv->lock, flags); > > +?????? reinit_completion(&priv->xfer_complete); > > + > > +?????? peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) | > > +?????????????????? FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) | > > +?????????????????? FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len); > > + > > +?????? writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); > > + > > +?????? memcpy_toio(priv->base + ASPEED_PECI_WR_DATA0, req->tx.buf, > > min_t(u8, req->tx.len, 16)); > > +?????? if (req->tx.len > 16) > > +?????????????? memcpy_toio(priv->base + ASPEED_PECI_WR_DATA4, req->tx.buf + > > 16, > > +?????????????????????????? req->tx.len - 16); > > + > > +?????? dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); > > +?????? print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req- > > >tx.len); > > On CONFIG_DYNAMIC_DEBUG=n builds the kernel will do all the work of > reading through this buffer, but skip emitting it. Are you sure you > want to pay that overhead for every transaction? I can remove it or I can add something like: #if IS_ENABLED(CONFIG_PECI_DEBUG) #define peci_debug(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) #else #define peci_debug(...) do { } while (0) #endif (and similar peci_trace with trace_printk for usage in IRQ handlers and such). What do you think? > > > + > > +?????? priv->status = 0; > > +?????? writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); > > +?????? spin_unlock_irqrestore(&priv->lock, flags); > > + > > +?????? ret = wait_for_completion_interruptible_timeout(&priv- > > >xfer_complete, timeout); > > spin_lock_irqsave() says "I don't know if interrupts are disabled > already, so I'll save the state, whatever it is, and restore later" > > wait_for_completion_interruptible_timeout() says "I know I am in a > sleepable context where interrupts are enabled" > > So, one of those is wrong, i.e. should it be spin_{lock,unlock}_irq()? You're right - I'll fix it. > > > > +?????? if (ret < 0) > > +?????????????? return ret; > > + > > +?????? if (ret == 0) { > > +?????????????? dev_dbg(priv->dev, "Timeout waiting for a response!\n"); > > +?????????????? return -ETIMEDOUT; > > +?????? } > > + > > +?????? spin_lock_irqsave(&priv->lock, flags); > > + > > +?????? writel(0, priv->base + ASPEED_PECI_CMD); > > + > > +?????? if (priv->status != ASPEED_PECI_INT_CMD_DONE) { > > +?????????????? spin_unlock_irqrestore(&priv->lock, flags); > > +?????????????? dev_dbg(priv->dev, "No valid response!\n"); > > +?????????????? return -EIO; > > +?????? } > > + > > +?????? spin_unlock_irqrestore(&priv->lock, flags); > > + > > +?????? memcpy_fromio(req->rx.buf, priv->base + ASPEED_PECI_RD_DATA0, > > min_t(u8, req->rx.len, 16)); > > +?????? if (req->rx.len > 16) > > +?????????????? memcpy_fromio(req->rx.buf + 16, priv->base + > > ASPEED_PECI_RD_DATA4, > > +???????????????????????????? req->rx.len - 16); > > + > > +?????? print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req- > > >rx.len); > > + > > +?????? return 0; > > +} > > + > > +static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) > > +{ > > +?????? struct aspeed_peci *priv = arg; > > +?????? u32 status; > > + > > +?????? spin_lock(&priv->lock); > > +?????? status = readl(priv->base + ASPEED_PECI_INT_STS); > > +?????? writel(status, priv->base + ASPEED_PECI_INT_STS); > > +?????? priv->status |= (status & ASPEED_PECI_INT_MASK); > > + > > +?????? /* > > +??????? * In most cases, interrupt bits will be set one by one but also > > note > > +??????? * that multiple interrupt bits could be set at the same time. > > +??????? */ > > +?????? if (status & ASPEED_PECI_INT_BUS_TIMEOUT) > > +?????????????? dev_dbg_ratelimited(priv->dev, > > "ASPEED_PECI_INT_BUS_TIMEOUT\n"); > > + > > +?????? if (status & ASPEED_PECI_INT_BUS_CONTENTION) > > +?????????????? dev_dbg_ratelimited(priv->dev, > > "ASPEED_PECI_INT_BUS_CONTENTION\n"); > > + > > +?????? if (status & ASPEED_PECI_INT_WR_FCS_BAD) > > +?????????????? dev_dbg_ratelimited(priv->dev, > > "ASPEED_PECI_INT_WR_FCS_BAD\n"); > > + > > +?????? if (status & ASPEED_PECI_INT_WR_FCS_ABORT) > > +?????????????? dev_dbg_ratelimited(priv->dev, > > "ASPEED_PECI_INT_WR_FCS_ABORT\n"); > > Are you sure these would not be better as tracepoints? If you're > debugging an interrupt related failure, the ratelimiting might get in > your way when you really need to know when one of these error > interrupts fire relative to another event. Tracepoints are ABI(ish), and using a full blown tracepoint just for IRQ status would probably be too much. I was thinking about something like trace_printk hidden under a "CONFIG_PECI_DEBUG" (see above), but perhaps that's something for the future improvement? > > > + > > +?????? /* > > +??????? * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE > > bit > > +??????? * set even in an error case. > > +??????? */ > > +?????? if (status & ASPEED_PECI_INT_CMD_DONE) > > +?????????????? complete(&priv->xfer_complete); > > Hmm, no need to check if there was a sequencing error, like a command > was never submitted? It's handled by checking if HW is idle in xfer before a command is sent, where we just expect a single interrupt per command. > > > + > > +?????? spin_unlock(&priv->lock); > > + > > +?????? return IRQ_HANDLED; > > +} > > + > > +static void aspeed_peci_property_sanitize(struct device *dev, const char > > *propname, > > +???????????????????????????????????????? u32 min, u32 max, u32 default_val, > > u32 *propval) > > +{ > > +?????? u32 val; > > +?????? int ret; > > + > > +?????? ret = device_property_read_u32(dev, propname, &val); > > +?????? if (ret) { > > +?????????????? val = default_val; > > +?????? } else if (val > max || val < min) { > > +?????????????? dev_warn(dev, "Invalid %s: %u, falling back to: %u\n", > > +??????????????????????? propname, val, default_val); > > + > > +?????????????? val = default_val; > > +?????? } > > + > > +?????? *propval = val; > > +} > > + > > +static void aspeed_peci_property_setup(struct aspeed_peci *priv) > > +{ > > +?????? aspeed_peci_property_sanitize(priv->dev, "aspeed,clock-divider", > > +???????????????????????????????????? 0, ASPEED_PECI_CLK_DIV_MAX, > > +???????????????????????????????????? ASPEED_PECI_CLK_DIV_DEFAULT, &priv- > > >clk_div); > > +?????? aspeed_peci_property_sanitize(priv->dev, "aspeed,msg-timing", > > +???????????????????????????????????? 0, ASPEED_PECI_MSG_TIMING_MAX, > > +???????????????????????????????????? ASPEED_PECI_MSG_TIMING_DEFAULT, &priv- > > >msg_timing); > > +?????? aspeed_peci_property_sanitize(priv->dev, "aspeed,addr-timing", > > +???????????????????????????????????? 0, ASPEED_PECI_ADDR_TIMING_MAX, > > +???????????????????????????????????? ASPEED_PECI_ADDR_TIMING_DEFAULT, > > &priv->addr_timing); > > +?????? aspeed_peci_property_sanitize(priv->dev, "aspeed,rd-sampling-point", > > +???????????????????????????????????? 0, ASPEED_PECI_RD_SAMPLING_POINT_MAX, > > +???????????????????????????????????? ASPEED_PECI_RD_SAMPLING_POINT_DEFAULT, > > +???????????????????????????????????? &priv->rd_sampling_point); > > +?????? aspeed_peci_property_sanitize(priv->dev, "cmd-timeout-ms", > > +???????????????????????????????????? 1, ASPEED_PECI_CMD_TIMEOUT_MS_MAX, > > +???????????????????????????????????? ASPEED_PECI_CMD_TIMEOUT_MS_DEFAULT, > > &priv->cmd_timeout_ms); > > +} > > + > > +static struct peci_controller_ops aspeed_ops = { > > +?????? .xfer = aspeed_peci_xfer, > > +}; > > + > > +static void aspeed_peci_reset_control_release(void *data) > > +{ > > +?????? reset_control_assert(data); > > +} > > + > > +int aspeed_peci_reset_control_deassert(struct device *dev, struct > > reset_control *rst) > > I'd recommend naming this devm_aspeed_peci_reset_control_deassert(), > because I came looking here from reading probe for why there was no > reassertion of reset on driver ->remove(). Ok. > > > +{ > > +?????? int ret; > > + > > +?????? ret = reset_control_deassert(rst); > > +?????? if (ret) > > +?????????????? return ret; > > + > > +?????? return devm_add_action_or_reset(dev, > > aspeed_peci_reset_control_release, rst); > > +} > > + > > +static void aspeed_peci_clk_release(void *data) > > +{ > > +?????? clk_disable_unprepare(data); > > +} > > + > > +static int aspeed_peci_clk_enable(struct device *dev, struct clk *clk) > > ...ditto on the devm prefix, just to speed readability. Ok. Thanks -Iwona > > > +{ > > +?????? int ret; > > + > > +?????? ret = clk_prepare_enable(clk); > > +?????? if (ret) > > +?????????????? return ret; > > + > > +?????? return devm_add_action_or_reset(dev, aspeed_peci_clk_release, clk); > > +} > > + > > +static int aspeed_peci_probe(struct platform_device *pdev) > > +{ > > +?????? struct peci_controller *controller; > > +?????? struct aspeed_peci *priv; > > +?????? int ret; > > + > > +?????? priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); > > +?????? if (!priv) > > +?????????????? return -ENOMEM; > > + > > +?????? priv->dev = &pdev->dev; > > +?????? dev_set_drvdata(priv->dev, priv); > > + > > +?????? priv->base = devm_platform_ioremap_resource(pdev, 0); > > +?????? if (IS_ERR(priv->base)) > > +?????????????? return PTR_ERR(priv->base); > > + > > +?????? priv->irq = platform_get_irq(pdev, 0); > > +?????? if (!priv->irq) > > +?????????????? return priv->irq; > > + > > +?????? ret = devm_request_irq(&pdev->dev, priv->irq, > > aspeed_peci_irq_handler, > > +????????????????????????????? 0, "peci-aspeed", priv); > > +?????? if (ret) > > +?????????????? return ret; > > + > > +?????? init_completion(&priv->xfer_complete); > > +?????? spin_lock_init(&priv->lock); > > + > > +?????? priv->rst = devm_reset_control_get(&pdev->dev, NULL); > > +?????? if (IS_ERR(priv->rst)) > > +?????????????? return dev_err_probe(priv->dev, PTR_ERR(priv->rst), > > +??????????????????????????????????? "failed to get reset control\n"); > > + > > +?????? ret = aspeed_peci_reset_control_deassert(priv->dev, priv->rst); > > +?????? if (ret) > > +?????????????? return dev_err_probe(priv->dev, ret, "cannot deassert reset > > control\n"); > > + > > +?????? priv->clk = devm_clk_get(priv->dev, NULL); > > +?????? if (IS_ERR(priv->clk)) > > +?????????????? return dev_err_probe(priv->dev, PTR_ERR(priv->clk), "failed > > to get clk\n"); > > + > > +?????? ret = aspeed_peci_clk_enable(priv->dev, priv->clk); > > +?????? if (ret) > > +?????????????? return dev_err_probe(priv->dev, ret, "failed to enable > > clock\n"); > > + > > +?????? aspeed_peci_property_setup(priv); > > + > > +?????? aspeed_peci_init_regs(priv); > > + > > +?????? controller = devm_peci_controller_add(priv->dev, &aspeed_ops); > > +?????? if (IS_ERR(controller)) > > +?????????????? return dev_err_probe(priv->dev, PTR_ERR(controller), > > +??????????????????????????????????? "failed to add aspeed peci > > controller\n"); > > + > > +?????? priv->controller = controller; > > + > > +?????? return 0; > > +} > > + > > +static const struct of_device_id aspeed_peci_of_table[] = { > > +?????? { .compatible = "aspeed,ast2400-peci", }, > > +?????? { .compatible = "aspeed,ast2500-peci", }, > > +?????? { .compatible = "aspeed,ast2600-peci", }, > > +?????? { } > > +}; > > +MODULE_DEVICE_TABLE(of, aspeed_peci_of_table); > > + > > +static struct platform_driver aspeed_peci_driver = { > > +?????? .probe? = aspeed_peci_probe, > > +?????? .driver = { > > +?????????????? .name?????????? = "peci-aspeed", > > +?????????????? .of_match_table = aspeed_peci_of_table, > > +?????? }, > > +}; > > +module_platform_driver(aspeed_peci_driver); > > + > > +MODULE_AUTHOR("Ryan Chen "); > > +MODULE_AUTHOR("Jae Hyun Yoo "); > > +MODULE_DESCRIPTION("ASPEED PECI driver"); > > +MODULE_LICENSE("GPL"); > > +MODULE_IMPORT_NS(PECI); > > -- > > 2.31.1 > > From jk at codeconstruct.com.au Fri Aug 27 13:08:31 2021 From: jk at codeconstruct.com.au (Jeremy Kerr) Date: Fri, 27 Aug 2021 11:08:31 +0800 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> <20210826061623.6352-4-chiawei_wang@aspeedtech.com> Message-ID: Hi Chia-Wei, > The Aspeed eSPI controller is slave device to communicate with > the master through the Enhanced Serial Peripheral Interface (eSPI). > All of the four eSPI channels, namely peripheral, virtual wire, > out-of-band, and flash are supported. Great to have this added submitted upstream! A few comments though: > --- > ?drivers/soc/aspeed/Kconfig???????????? |? 11 + > ?drivers/soc/aspeed/Makefile??????????? |?? 1 + > ?drivers/soc/aspeed/aspeed-espi-ctrl.c? | 205 +++++++++ > ?drivers/soc/aspeed/aspeed-espi-ctrl.h? | 304 ++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-ioc.h?? | 153 +++++++ > ?drivers/soc/aspeed/aspeed-espi-oob.h?? | 611 +++++++++++++++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-perif.h | 539 ++++++++++++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-vw.h??? | 142 ++++++ This structure is a bit odd - you have the one -crtl.c file, which defines the actual driver, but then a bunch of headers that contain more code than header-type definitions. Is there any reason that -flash, -ioc, -oob, -perif and -vw components can't be standard .c files? Then, for the userspace ABI: it looks like you're exposing everything through new device-specific ioctls. Would it not make more sense to use existing interfaces? For example, the virtual wire bits could be regular GPIOs; the flash interface could be a mtd or block device. I understand that we'll likely still need some level of custom device control, but the more we can use generic interfaces for, the less custom code (and interfaces) we'll need on the userspace side. Cheers, Jeremy -------------- next part -------------- An HTML attachment was scrubbed... URL: From chiawei_wang at aspeedtech.com Fri Aug 27 13:08:19 2021 From: chiawei_wang at aspeedtech.com (ChiaWei Wang) Date: Fri, 27 Aug 2021 03:08:19 +0000 Subject: [PATCH v3 1/4] dt-bindings: aspeed: Add eSPI controller In-Reply-To: <1629984361.334223.1033382.nullmailer@robh.at.kernel.org> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> <20210826061623.6352-2-chiawei_wang@aspeedtech.com> <1629984361.334223.1033382.nullmailer@robh.at.kernel.org> Message-ID: > From: Rob Herring > Sent: Thursday, August 26, 2021 9:26 PM > > On Thu, 26 Aug 2021 14:16:20 +0800, Chia-Wei Wang wrote: > > Add dt-bindings for Aspeed eSPI controller > > > > Signed-off-by: Chia-Wei Wang > > --- > > .../devicetree/bindings/soc/aspeed/espi.yaml | 157 > > ++++++++++++++++++ > > 1 file changed, 157 insertions(+) > > create mode 100644 > > Documentation/devicetree/bindings/soc/aspeed/espi.yaml > > > > My bot found errors running 'make DT_CHECKER_FLAGS=-m > dt_binding_check' > on your patch (DT_CHECKER_FLAGS is new in v5.13): > > yamllint warnings/errors: > > dtschema/dtc warnings/errors: > /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/as > peed/espi.example.dt.yaml: espi at 1e6ee000: 'espi-ctrl at 0', 'espi-mmbi at 800' > do not match any of the regexes: 'pinctrl-[0-9]+' > From schema: > /builds/robherring/linux-dt-review/Documentation/devicetree/bindings/soc/as > peed/espi.yaml > > doc reference errors (make refcheckdocs): > > See https://patchwork.ozlabs.org/patch/1520968 > > This check can fail if there are any dependencies. The base for a patch series is > generally the most recent rc1. > > If you already ran 'make dt_binding_check' and didn't see the above error(s), > then make sure 'yamllint' is installed and dt-schema is up to > date: > > pip3 install dtschema --upgrade > > Please check and re-submit. The warning has been fixed by using patternProperties to define the espi-ctrl and the espi-mmbi child nodes. The updated version will be submitted soon. Thanks. Chiawei From jk at codeconstruct.com.au Fri Aug 27 13:20:05 2021 From: jk at codeconstruct.com.au (Jeremy Kerr) Date: Fri, 27 Aug 2021 11:20:05 +0800 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> <20210826061623.6352-4-chiawei_wang@aspeedtech.com> Message-ID: <7e7378c49ecfb21fef6a0640f92c1b3a7a5878d0.camel@codeconstruct.com.au> Hi Chia-Wei, [apologies for the re-send, dropping HTML part...] > The Aspeed eSPI controller is slave device to communicate with > the master through the Enhanced Serial Peripheral Interface (eSPI). > All of the four eSPI channels, namely peripheral, virtual wire, > out-of-band, and flash are supported. Great to have this added submitted upstream! A few comments though: > --- > ?drivers/soc/aspeed/Kconfig???????????? |? 11 + > ?drivers/soc/aspeed/Makefile??????????? |?? 1 + > ?drivers/soc/aspeed/aspeed-espi-ctrl.c? | 205 +++++++++ > ?drivers/soc/aspeed/aspeed-espi-ctrl.h? | 304 ++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-ioc.h?? | 153 +++++++ > ?drivers/soc/aspeed/aspeed-espi-oob.h?? | 611 +++++++++++++++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-perif.h | 539 ++++++++++++++++++++++ > ?drivers/soc/aspeed/aspeed-espi-vw.h??? | 142 ++++++ This structure is a bit odd - you have the one -crtl.c file, which defines the actual driver, but then a bunch of headers that contain more code than header-type definitions. Is there any reason that -flash, -ioc, -oob, -perif and -vw components can't be standard .c files? Then, for the userspace ABI: it looks like you're exposing everything through new device-specific ioctls. Would it not make more sense to use existing interfaces? For example, the virtual wire bits could be regular GPIOs; the flash interface could be a mtd or block device. I understand that we'll likely still need some level of custom device control, but the more we can use generic interfaces for, the less custom code (and interfaces) we'll need on the userspace side. Cheers, Jeremy From chiawei_wang at aspeedtech.com Fri Aug 27 13:48:39 2021 From: chiawei_wang at aspeedtech.com (ChiaWei Wang) Date: Fri, 27 Aug 2021 03:48:39 +0000 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <7e7378c49ecfb21fef6a0640f92c1b3a7a5878d0.camel@codeconstruct.com.au> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> <20210826061623.6352-4-chiawei_wang@aspeedtech.com> <7e7378c49ecfb21fef6a0640f92c1b3a7a5878d0.camel@codeconstruct.com.au> Message-ID: Hi Jeremy, Thanks for reviewing the patch. > From: Jeremy Kerr > Sent: Friday, August 27, 2021 11:20 AM > > Hi Chia-Wei, > > [apologies for the re-send, dropping HTML part...] > > > The Aspeed eSPI controller is slave device to communicate with the > > master through the Enhanced Serial Peripheral Interface (eSPI). > > All of the four eSPI channels, namely peripheral, virtual wire, > > out-of-band, and flash are supported. > > Great to have this added submitted upstream! A few comments though: > > > --- > > ?drivers/soc/aspeed/Kconfig???????????? |? 11 + > > ?drivers/soc/aspeed/Makefile??????????? |?? 1 + > > ?drivers/soc/aspeed/aspeed-espi-ctrl.c? | 205 +++++++++ > > ?drivers/soc/aspeed/aspeed-espi-ctrl.h? | 304 ++++++++++++ > > ?drivers/soc/aspeed/aspeed-espi-flash.h | 380 +++++++++++++++ > > ?drivers/soc/aspeed/aspeed-espi-ioc.h?? | 153 +++++++ > > ?drivers/soc/aspeed/aspeed-espi-oob.h?? | 611 > > +++++++++++++++++++++++++ > > ?drivers/soc/aspeed/aspeed-espi-perif.h | 539 ++++++++++++++++++++++ > > ?drivers/soc/aspeed/aspeed-espi-vw.h??? | 142 ++++++ > > This structure is a bit odd - you have the one -crtl.c file, which defines the > actual driver, but then a bunch of headers that contain more code than > header-type definitions. > > Is there any reason that -flash, -ioc, -oob, -perif and -vw components can't be > standard .c files? The eSPI slave device comprises four channels, where each of them has individual functionality. Putting the four channels driver code into a single file makes it hard to maintain and trace. We did consider to make them standard .c files. But it requires to export channel functions into kernel space although they are dedicated only to this eSPI driver. As espi-ctrl needs to invoke corresponding channel functions when it is interrupted by eSPI events. To avoid polluting kernel space, we decided to put driver code in header files and make the channel functions 'static'. BTW, I once encountered .c file inclusion in other projects. Is it proper for Linux driver development? > > Then, for the userspace ABI: it looks like you're exposing everything through > new device-specific ioctls. Would it not make more sense to use existing > interfaces? For example, the virtual wire bits could be regular GPIOs; the flash > interface could be a mtd or block device. > > I understand that we'll likely still need some level of custom device control, but > the more we can use generic interfaces for, the less custom code (and > interfaces) we'll need on the userspace side. > eSPI communication is based on the its cycle packet format. We intended to let userspace decided how to interpret and compose TX/RX packets including header, tag, length (encoded), and data. IOCTL comes to our first mind as it also works in the 'packet' like paradigm. Chiawei From chiawei_wang at aspeedtech.com Fri Aug 27 13:52:03 2021 From: chiawei_wang at aspeedtech.com (ChiaWei Wang) Date: Fri, 27 Aug 2021 03:52:03 +0000 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <202108270732.OvBQ4K3D-lkp@intel.com> References: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> <202108270732.OvBQ4K3D-lkp@intel.com> Message-ID: Aspeed 5th and 6th generation SoCs are based on the ARM 32-bits architecture. Should we follow the report to make the driver 64-bits compatible? Or revise the driver to use more specific data types? Thanks. Chiawei > From: kernel test robot > Sent: Friday, August 27, 2021 7:30 AM > > Hi Chia-Wei, > > I love your patch! Yet something to improve: > > [auto build test ERROR on robh/for-next] [also build test ERROR on > arm/for-next keystone/next soc/for-next rockchip/for-next > arm64/for-next/core linus/master joel-aspeed/for-next v5.14-rc7 > next-20210826] [cannot apply to xlnx/master] [If your patch is applied to the > wrong git tree, kindly drop us a note. > And when submitting patch, we suggest to use '--base' as documented in > https://git-scm.com/docs/git-format-patch] > > url: > https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/arm-aspeed-Add-e > SPI-support/20210826-141737 > base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next > config: arm64-randconfig-r002-20210826 (attached as .config) > compiler: aarch64-linux-gcc (GCC) 11.2.0 reproduce (this is a W=1 build): > wget > https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O > ~/bin/make.cross > chmod +x ~/bin/make.cross > # > https://github.com/0day-ci/linux/commit/2980a1777c50754fe145f2e73ded873 > 9931c0712 > git remote add linux-review https://github.com/0day-ci/linux > git fetch --no-tags linux-review > Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737 > git checkout 2980a1777c50754fe145f2e73ded8739931c0712 > # save the attached .config to linux build tree > COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 > make.cross ARCH=arm64 > > If you fix the issue, kindly add following tag as appropriate > Reported-by: kernel test robot > > All errors (new ones prefixed by >>): > > In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:22: > drivers/soc/aspeed/aspeed-espi-perif.h: In function > 'aspeed_espi_perif_alloc': > >> drivers/soc/aspeed/aspeed-espi-perif.h:446:43: error: passing > >> argument 3 of 'of_property_read_u32' from incompatible pointer type > >> [-Werror=incompatible-pointer-types] > 446 | > &espi_perif->mcyc_saddr); > | > ^~~~~~~~~~~~~~~~~~~~~~~ > | | > | phys_addr_t > * {aka long long unsigned int *} > In file included from include/linux/of_device.h:9, > from drivers/soc/aspeed/aspeed-espi-ctrl.c:9: > include/linux/of.h:1249:45: note: expected 'u32 *' {aka 'unsigned int *'} but > argument is of type 'phys_addr_t *' {aka 'long long unsigned int *'} > 1249 | u32 *out_value) > | ~~~~~^~~~~~~~~ > drivers/soc/aspeed/aspeed-espi-ctrl.c: In function > 'aspeed_espi_ctrl_probe': > drivers/soc/aspeed/aspeed-espi-ctrl.c:98:30: warning: cast from pointer to > integer of different size [-Wpointer-to-int-cast] > 98 | espi_ctrl->version = > (uint32_t)of_device_get_match_data(dev); > | ^ > cc1: some warnings being treated as errors > > > vim +/of_property_read_u32 +446 drivers/soc/aspeed/aspeed-espi-perif.h > > 422 > 423 static void *aspeed_espi_perif_alloc(struct device *dev, struct > aspeed_espi_ctrl *espi_ctrl) > 424 { > 425 int rc; > 426 struct aspeed_espi_perif *espi_perif; > 427 struct aspeed_espi_perif_dma *dma; > 428 > 429 espi_perif = devm_kzalloc(dev, sizeof(*espi_perif), > GFP_KERNEL); > 430 if (!espi_perif) > 431 return ERR_PTR(-ENOMEM); > 432 > 433 espi_perif->ctrl = espi_ctrl; > 434 > 435 init_waitqueue_head(&espi_perif->wq); > 436 > 437 spin_lock_init(&espi_perif->lock); > 438 > 439 mutex_init(&espi_perif->pc_rx_mtx); > 440 mutex_init(&espi_perif->pc_tx_mtx); > 441 mutex_init(&espi_perif->np_tx_mtx); > 442 > 443 espi_perif->mcyc_enable = > of_property_read_bool(dev->of_node, "perif,memcyc-enable"); > 444 if (espi_perif->mcyc_enable) { > 445 rc = of_property_read_u32(dev->of_node, > "perif,memcyc-src-addr", > > 446 &espi_perif->mcyc_saddr); > 447 if (rc) { > 448 dev_err(dev, "cannot get Host source address for > memory cycle\n"); > 449 return ERR_PTR(-ENODEV); > 450 } > 451 > 452 rc = of_property_read_u32(dev->of_node, > "perif,memcyc-size", > 453 &espi_perif->mcyc_size); > 454 if (rc) { > 455 dev_err(dev, "cannot get size for memory cycle\n"); > 456 return ERR_PTR(-ENODEV); > 457 } > 458 > 459 if (espi_perif->mcyc_size < PERIF_MEMCYC_SIZE_MIN) > 460 espi_perif->mcyc_size = PERIF_MEMCYC_SIZE_MIN; > 461 else > 462 espi_perif->mcyc_size = > roundup_pow_of_two(espi_perif->mcyc_size); > 463 > 464 espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1); > 465 espi_perif->mcyc_virt = dma_alloc_coherent(dev, > espi_perif->mcyc_size, > 466 &espi_perif->mcyc_taddr, > GFP_KERNEL); > 467 if (!espi_perif->mcyc_virt) { > 468 dev_err(dev, "cannot allocate memory cycle > region\n"); > 469 return ERR_PTR(-ENOMEM); > 470 } > 471 } > 472 > 473 if (of_property_read_bool(dev->of_node, "perif,dma-mode")) { > 474 dma = &espi_perif->dma; > 475 > 476 dma->pc_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, > 477 &dma->pc_tx_addr, > GFP_KERNEL); > 478 if (!dma->pc_tx_virt) { > 479 dev_err(dev, "cannot allocate posted TX DMA > buffer\n"); > 480 return ERR_PTR(-ENOMEM); > 481 } > 482 > 483 dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE, > 484 &dma->pc_rx_addr, > GFP_KERNEL); > 485 if (!dma->pc_rx_virt) { > 486 dev_err(dev, "cannot allocate posted RX DMA > buffer\n"); > 487 return ERR_PTR(-ENOMEM); > 488 } > 489 > 490 dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, > 491 &dma->np_tx_addr, GFP_KERNEL); > 492 if (!dma->np_tx_virt) { > 493 dev_err(dev, "cannot allocate non-posted TX DMA > buffer\n"); > 494 return ERR_PTR(-ENOMEM); > 495 } > 496 > 497 espi_perif->dma_mode = 1; > 498 } > 499 > 500 espi_perif->mdev.parent = dev; > 501 espi_perif->mdev.minor = MISC_DYNAMIC_MINOR; > 502 espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, > "%s", PERIF_MDEV_NAME); > 503 espi_perif->mdev.fops = &aspeed_espi_perif_fops; > 504 rc = misc_register(&espi_perif->mdev); > 505 if (rc) { > 506 dev_err(dev, "cannot register device\n"); > 507 return ERR_PTR(rc); > 508 } > 509 > 510 aspeed_espi_perif_enable(espi_perif); > 511 > 512 return espi_perif; > 513 } > 514 > > --- > 0-DAY CI Kernel Test Service, Intel Corporation > https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org From jk at codeconstruct.com.au Fri Aug 27 14:36:37 2021 From: jk at codeconstruct.com.au (Jeremy Kerr) Date: Fri, 27 Aug 2021 12:36:37 +0800 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> <20210826061623.6352-4-chiawei_wang@aspeedtech.com> <7e7378c49ecfb21fef6a0640f92c1b3a7a5878d0.camel@codeconstruct.com.au> Message-ID: <3f2feea6c2fb21c2fdcb419cdc7ceddf3ade06ee.camel@codeconstruct.com.au> Hi Chiawei, > The eSPI slave device comprises four channels, where each of them has > individual functionality. Putting the four channels driver code into a > single file makes it hard to maintain and trace. Yep, understood. > We did consider to make them standard .c files. > But it requires to export channel functions into kernel space > although they are dedicated only to this eSPI driver. What do you mean by "export into kernel space" here? The function prototypes need to be available to your main (-ctrl.c) file, regardless of whether you're putting the entire functions in a header file, or just the prototype. There's doesn't need to be any difference in visibility outside of your own module if you were to do this the usual way. > As espi-ctrl needs to invoke corresponding channel functions when it > is interrupted by eSPI events. > > To avoid polluting kernel space, we decided to put driver code in > header files and make the channel functions 'static'. > > BTW, I once encountered .c file inclusion in other projects. Is it > proper for Linux driver development? It can be, just that in this case it's a bit unusual, and I can't see a good reason for doing so. This could just be a standard multiple-source-file module. > eSPI communication is based on the its cycle packet format. > We intended to let userspace decided how to interpret and compose > TX/RX packets including header, tag, length (encoded), and data. > IOCTL comes to our first mind as it also works in the 'packet' like > paradigm. But you're not always exposing a packet-like interface for this. For example, your virtual-wire interface just has a get/set interface for bits in a register (plus some PCH event handling, which may not be applicable to all platforms...). The other channels do look like more of a packet interface though, but in that case I'm not convinced that an ioctl interface is the best way to go for that. You're essentially sending a (length, pointer) pair over the ioctls there, which sounds more like a write() than an ioctl(). Regardless of the choice of interface though, this will definitely need some documentation or description of the API, and the ioc header to be somewhere useful for userspace to consume. With that documented, we'd have a better idea of how the new ABI is supposed to work. Cheers, Jeremy From lkp at intel.com Fri Aug 27 06:05:57 2021 From: lkp at intel.com (kernel test robot) Date: Fri, 27 Aug 2021 04:05:57 +0800 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> Message-ID: <202108270339.2LOAeemT-lkp@intel.com> Hi Chia-Wei, I love your patch! Perhaps something to improve: [auto build test WARNING on robh/for-next] [also build test WARNING on arm/for-next keystone/next soc/for-next rockchip/for-next arm64/for-next/core linus/master joel-aspeed/for-next v5.14-rc7 next-20210826] [cannot apply to xlnx/master] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737 base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next config: arm64-randconfig-r025-20210826 (attached as .config) compiler: clang version 14.0.0 (https://github.com/llvm/llvm-project ea08c4cd1c0869ec5024a8bb3f5cdf06ab03ae83) reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # install arm64 cross compiling tool for clang build # apt-get install binutils-aarch64-linux-gnu # https://github.com/0day-ci/linux/commit/2980a1777c50754fe145f2e73ded8739931c0712 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737 git checkout 2980a1777c50754fe145f2e73ded8739931c0712 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=arm64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot All warnings (new ones prefixed by >>): In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:22: drivers/soc/aspeed/aspeed-espi-perif.h:446:8: error: incompatible pointer types passing 'phys_addr_t *' (aka 'unsigned long long *') to parameter of type 'u32 *' (aka 'unsigned int *') [-Werror,-Wincompatible-pointer-types] &espi_perif->mcyc_saddr); ^~~~~~~~~~~~~~~~~~~~~~~ include/linux/of.h:1249:17: note: passing argument to parameter 'out_value' here u32 *out_value) ^ >> drivers/soc/aspeed/aspeed-espi-ctrl.c:98:23: warning: cast to smaller integer type 'uint32_t' (aka 'unsigned int') from 'const void *' [-Wvoid-pointer-to-int-cast] espi_ctrl->version = (uint32_t)of_device_get_match_data(dev); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1 warning and 1 error generated. vim +98 drivers/soc/aspeed/aspeed-espi-ctrl.c 87 88 static int aspeed_espi_ctrl_probe(struct platform_device *pdev) 89 { 90 int rc = 0; 91 struct aspeed_espi_ctrl *espi_ctrl; 92 struct device *dev = &pdev->dev; 93 94 espi_ctrl = devm_kzalloc(dev, sizeof(*espi_ctrl), GFP_KERNEL); 95 if (!espi_ctrl) 96 return -ENOMEM; 97 > 98 espi_ctrl->version = (uint32_t)of_device_get_match_data(dev); 99 100 espi_ctrl->map = syscon_node_to_regmap(dev->parent->of_node); 101 if (IS_ERR(espi_ctrl->map)) { 102 dev_err(dev, "cannot get remap\n"); 103 return -ENODEV; 104 } 105 106 espi_ctrl->irq = platform_get_irq(pdev, 0); 107 if (espi_ctrl->irq < 0) 108 return espi_ctrl->irq; 109 110 espi_ctrl->clk = devm_clk_get(dev, NULL); 111 if (IS_ERR(espi_ctrl->clk)) { 112 dev_err(dev, "cannot get clock\n"); 113 return -ENODEV; 114 } 115 116 rc = clk_prepare_enable(espi_ctrl->clk); 117 if (rc) { 118 dev_err(dev, "cannot enable clock\n"); 119 return rc; 120 } 121 122 espi_ctrl->perif = aspeed_espi_perif_alloc(dev, espi_ctrl); 123 if (IS_ERR(espi_ctrl->perif)) { 124 dev_err(dev, "failed to allocate peripheral channel\n"); 125 return PTR_ERR(espi_ctrl->perif); 126 } 127 128 espi_ctrl->vw = aspeed_espi_vw_alloc(dev, espi_ctrl); 129 if (IS_ERR(espi_ctrl->vw)) { 130 dev_err(dev, "failed to allocate virtual wire channel\n"); 131 return PTR_ERR(espi_ctrl->vw); 132 } 133 134 espi_ctrl->oob = aspeed_espi_oob_alloc(dev, espi_ctrl); 135 if (IS_ERR(espi_ctrl->oob)) { 136 dev_err(dev, "failed to allocate out-of-band channel\n"); 137 return PTR_ERR(espi_ctrl->oob); 138 } 139 140 espi_ctrl->flash = aspeed_espi_flash_alloc(dev, espi_ctrl); 141 if (rc) { 142 dev_err(dev, "failed to allocate flash channel\n"); 143 return PTR_ERR(espi_ctrl->flash); 144 } 145 146 regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T0, 0x0); 147 regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_T1, 0x0); 148 regmap_write(espi_ctrl->map, ESPI_SYSEVT_INT_EN, 0xffffffff); 149 150 regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_T0, 0x1); 151 regmap_write(espi_ctrl->map, ESPI_SYSEVT1_INT_EN, 0x1); 152 153 rc = devm_request_irq(dev, espi_ctrl->irq, 154 aspeed_espi_ctrl_isr, 155 0, DEVICE_NAME, espi_ctrl); 156 if (rc) { 157 dev_err(dev, "failed to request IRQ\n"); 158 return rc; 159 } 160 161 regmap_update_bits(espi_ctrl->map, ESPI_INT_EN, 162 ESPI_INT_EN_HW_RST_DEASSERT, 163 ESPI_INT_EN_HW_RST_DEASSERT); 164 165 dev_set_drvdata(dev, espi_ctrl); 166 167 dev_info(dev, "module loaded\n"); 168 169 return 0; 170 } 171 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org -------------- next part -------------- A non-text attachment was scrubbed... Name: .config.gz Type: application/gzip Size: 35430 bytes Desc: not available URL: From lkp at intel.com Fri Aug 27 09:30:06 2021 From: lkp at intel.com (kernel test robot) Date: Fri, 27 Aug 2021 07:30:06 +0800 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> References: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> Message-ID: <202108270732.OvBQ4K3D-lkp@intel.com> Hi Chia-Wei, I love your patch! Yet something to improve: [auto build test ERROR on robh/for-next] [also build test ERROR on arm/for-next keystone/next soc/for-next rockchip/for-next arm64/for-next/core linus/master joel-aspeed/for-next v5.14-rc7 next-20210826] [cannot apply to xlnx/master] [If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in https://git-scm.com/docs/git-format-patch] url: https://github.com/0day-ci/linux/commits/Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737 base: https://git.kernel.org/pub/scm/linux/kernel/git/robh/linux.git for-next config: arm64-randconfig-r002-20210826 (attached as .config) compiler: aarch64-linux-gcc (GCC) 11.2.0 reproduce (this is a W=1 build): wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross # https://github.com/0day-ci/linux/commit/2980a1777c50754fe145f2e73ded8739931c0712 git remote add linux-review https://github.com/0day-ci/linux git fetch --no-tags linux-review Chia-Wei-Wang/arm-aspeed-Add-eSPI-support/20210826-141737 git checkout 2980a1777c50754fe145f2e73ded8739931c0712 # save the attached .config to linux build tree COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arm64 If you fix the issue, kindly add following tag as appropriate Reported-by: kernel test robot All errors (new ones prefixed by >>): In file included from drivers/soc/aspeed/aspeed-espi-ctrl.c:22: drivers/soc/aspeed/aspeed-espi-perif.h: In function 'aspeed_espi_perif_alloc': >> drivers/soc/aspeed/aspeed-espi-perif.h:446:43: error: passing argument 3 of 'of_property_read_u32' from incompatible pointer type [-Werror=incompatible-pointer-types] 446 | &espi_perif->mcyc_saddr); | ^~~~~~~~~~~~~~~~~~~~~~~ | | | phys_addr_t * {aka long long unsigned int *} In file included from include/linux/of_device.h:9, from drivers/soc/aspeed/aspeed-espi-ctrl.c:9: include/linux/of.h:1249:45: note: expected 'u32 *' {aka 'unsigned int *'} but argument is of type 'phys_addr_t *' {aka 'long long unsigned int *'} 1249 | u32 *out_value) | ~~~~~^~~~~~~~~ drivers/soc/aspeed/aspeed-espi-ctrl.c: In function 'aspeed_espi_ctrl_probe': drivers/soc/aspeed/aspeed-espi-ctrl.c:98:30: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast] 98 | espi_ctrl->version = (uint32_t)of_device_get_match_data(dev); | ^ cc1: some warnings being treated as errors vim +/of_property_read_u32 +446 drivers/soc/aspeed/aspeed-espi-perif.h 422 423 static void *aspeed_espi_perif_alloc(struct device *dev, struct aspeed_espi_ctrl *espi_ctrl) 424 { 425 int rc; 426 struct aspeed_espi_perif *espi_perif; 427 struct aspeed_espi_perif_dma *dma; 428 429 espi_perif = devm_kzalloc(dev, sizeof(*espi_perif), GFP_KERNEL); 430 if (!espi_perif) 431 return ERR_PTR(-ENOMEM); 432 433 espi_perif->ctrl = espi_ctrl; 434 435 init_waitqueue_head(&espi_perif->wq); 436 437 spin_lock_init(&espi_perif->lock); 438 439 mutex_init(&espi_perif->pc_rx_mtx); 440 mutex_init(&espi_perif->pc_tx_mtx); 441 mutex_init(&espi_perif->np_tx_mtx); 442 443 espi_perif->mcyc_enable = of_property_read_bool(dev->of_node, "perif,memcyc-enable"); 444 if (espi_perif->mcyc_enable) { 445 rc = of_property_read_u32(dev->of_node, "perif,memcyc-src-addr", > 446 &espi_perif->mcyc_saddr); 447 if (rc) { 448 dev_err(dev, "cannot get Host source address for memory cycle\n"); 449 return ERR_PTR(-ENODEV); 450 } 451 452 rc = of_property_read_u32(dev->of_node, "perif,memcyc-size", 453 &espi_perif->mcyc_size); 454 if (rc) { 455 dev_err(dev, "cannot get size for memory cycle\n"); 456 return ERR_PTR(-ENODEV); 457 } 458 459 if (espi_perif->mcyc_size < PERIF_MEMCYC_SIZE_MIN) 460 espi_perif->mcyc_size = PERIF_MEMCYC_SIZE_MIN; 461 else 462 espi_perif->mcyc_size = roundup_pow_of_two(espi_perif->mcyc_size); 463 464 espi_perif->mcyc_mask = ~(espi_perif->mcyc_size - 1); 465 espi_perif->mcyc_virt = dma_alloc_coherent(dev, espi_perif->mcyc_size, 466 &espi_perif->mcyc_taddr, GFP_KERNEL); 467 if (!espi_perif->mcyc_virt) { 468 dev_err(dev, "cannot allocate memory cycle region\n"); 469 return ERR_PTR(-ENOMEM); 470 } 471 } 472 473 if (of_property_read_bool(dev->of_node, "perif,dma-mode")) { 474 dma = &espi_perif->dma; 475 476 dma->pc_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, 477 &dma->pc_tx_addr, GFP_KERNEL); 478 if (!dma->pc_tx_virt) { 479 dev_err(dev, "cannot allocate posted TX DMA buffer\n"); 480 return ERR_PTR(-ENOMEM); 481 } 482 483 dma->pc_rx_virt = dma_alloc_coherent(dev, PAGE_SIZE, 484 &dma->pc_rx_addr, GFP_KERNEL); 485 if (!dma->pc_rx_virt) { 486 dev_err(dev, "cannot allocate posted RX DMA buffer\n"); 487 return ERR_PTR(-ENOMEM); 488 } 489 490 dma->np_tx_virt = dma_alloc_coherent(dev, PAGE_SIZE, 491 &dma->np_tx_addr, GFP_KERNEL); 492 if (!dma->np_tx_virt) { 493 dev_err(dev, "cannot allocate non-posted TX DMA buffer\n"); 494 return ERR_PTR(-ENOMEM); 495 } 496 497 espi_perif->dma_mode = 1; 498 } 499 500 espi_perif->mdev.parent = dev; 501 espi_perif->mdev.minor = MISC_DYNAMIC_MINOR; 502 espi_perif->mdev.name = devm_kasprintf(dev, GFP_KERNEL, "%s", PERIF_MDEV_NAME); 503 espi_perif->mdev.fops = &aspeed_espi_perif_fops; 504 rc = misc_register(&espi_perif->mdev); 505 if (rc) { 506 dev_err(dev, "cannot register device\n"); 507 return ERR_PTR(rc); 508 } 509 510 aspeed_espi_perif_enable(espi_perif); 511 512 return espi_perif; 513 } 514 --- 0-DAY CI Kernel Test Service, Intel Corporation https://lists.01.org/hyperkitty/list/kbuild-all at lists.01.org -------------- next part -------------- A non-text attachment was scrubbed... Name: .config.gz Type: application/gzip Size: 42142 bytes Desc: not available URL: From joel at jms.id.au Fri Aug 27 15:48:50 2021 From: joel at jms.id.au (Joel Stanley) Date: Fri, 27 Aug 2021 05:48:50 +0000 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: References: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> <202108270732.OvBQ4K3D-lkp@intel.com> Message-ID: On Fri, 27 Aug 2021 at 03:52, ChiaWei Wang wrote: > > Aspeed 5th and 6th generation SoCs are based on the ARM 32-bits architecture. > Should we follow the report to make the driver 64-bits compatible? > Or revise the driver to use more specific data types? Yes, in general it's expected your driver will compile cleanly for 64-bit architectures. This helps with testing and static analysis, where CI builds all the drivers for x86. Cheers, Joel From chiawei_wang at aspeedtech.com Fri Aug 27 18:49:16 2021 From: chiawei_wang at aspeedtech.com (ChiaWei Wang) Date: Fri, 27 Aug 2021 08:49:16 +0000 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: References: <20210826061623.6352-4-chiawei_wang@aspeedtech.com> <202108270732.OvBQ4K3D-lkp@intel.com> Message-ID: > From: Joel Stanley > Sent: Friday, August 27, 2021 1:49 PM > > On Fri, 27 Aug 2021 at 03:52, ChiaWei Wang > wrote: > > > > Aspeed 5th and 6th generation SoCs are based on the ARM 32-bits > architecture. > > Should we follow the report to make the driver 64-bits compatible? > > Or revise the driver to use more specific data types? > > Yes, in general it's expected your driver will compile cleanly for 64-bit > architectures. This helps with testing and static analysis, where CI builds all the > drivers for x86. Understood. Will fix the data type issue in the next submission. Thanks. Chiawei From chiawei_wang at aspeedtech.com Fri Aug 27 18:49:13 2021 From: chiawei_wang at aspeedtech.com (ChiaWei Wang) Date: Fri, 27 Aug 2021 08:49:13 +0000 Subject: [PATCH v3 3/4] soc: aspeed: Add eSPI driver In-Reply-To: <3f2feea6c2fb21c2fdcb419cdc7ceddf3ade06ee.camel@codeconstruct.com.au> References: <20210826061623.6352-1-chiawei_wang@aspeedtech.com> <20210826061623.6352-4-chiawei_wang@aspeedtech.com> <7e7378c49ecfb21fef6a0640f92c1b3a7a5878d0.camel@codeconstruct.com.au> <3f2feea6c2fb21c2fdcb419cdc7ceddf3ade06ee.camel@codeconstruct.com.au> Message-ID: Hi Jeremy > From: Jeremy Kerr > Sent: Friday, August 27, 2021 12:37 PM > > Hi Chiawei, > > > The eSPI slave device comprises four channels, where each of them has > > individual functionality. Putting the four channels driver code into > > a single file makes it hard to maintain and trace. > > Yep, understood. > > > We did consider to make them standard .c files. > > But it requires to export channel functions into kernel space although > > they are dedicated only to this eSPI driver. > > What do you mean by "export into kernel space" here? The function prototypes The channel functions will be visible to all kernel driver files. > need to be available to your main (-ctrl.c) file, regardless of whether you're > putting the entire functions in a header file, or just the prototype. There's > doesn't need to be any difference in visibility outside of your own module if > you were to do this the usual way. Maybe I was trying to make channels function visible only to espi-ctrl.c too far. I will revise the driver to present in the usual .c way. > > > As espi-ctrl needs to invoke corresponding channel functions when it > > is interrupted by eSPI events. > > > > To avoid polluting kernel space, we decided to put driver code in > > header files and make the channel functions 'static'. > > > > BTW, I once encountered .c file inclusion in other projects. Is it > > proper for Linux driver development? > > It can be, just that in this case it's a bit unusual, and I can't see a good reason > for doing so. This could just be a standard multiple-source-file module. > > > eSPI communication is based on the its cycle packet format. > > We intended to let userspace decided how to interpret and compose > > TX/RX packets including header, tag, length (encoded), and data. > > IOCTL comes to our first mind as it also works in the 'packet' like > > paradigm. > > But you're not always exposing a packet-like interface for this. For example, > your virtual-wire interface just has a get/set interface for bits in a register > (plus some PCH event handling, which may not be applicable to all > platforms...). > > The other channels do look like more of a packet interface though, but in that > case I'm not convinced that an ioctl interface is the best way to go for that. > You're essentially sending a (length, pointer) pair over the ioctls there, which > sounds more like a write() than an ioctl(). In most cases, yes. Currently only the peripheral channel has more than the 2 (put tx/get rx) IOCTL code. We think it might be a good idea to make the user interfaces of all channels consistent using IOCTL. > > Regardless of the choice of interface though, this will definitely need some > documentation or description of the API, and the ioc header to be somewhere > useful for userspace to consume. > > With that documented, we'd have a better idea of how the new ABI is > supposed to work. Sure. more comments will be added in aspeed-espi-ioc.h to describe the usage and the purpose. Thanks for your feedback. Chiawei From dan.j.williams at intel.com Sat Aug 28 02:24:49 2021 From: dan.j.williams at intel.com (Dan Williams) Date: Fri, 27 Aug 2021 09:24:49 -0700 Subject: [PATCH v2 07/15] peci: Add peci-aspeed controller driver In-Reply-To: References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-8-iwona.winiarska@intel.com> Message-ID: On Thu, Aug 26, 2021 at 4:55 PM Winiarska, Iwona wrote: > > On Wed, 2021-08-25 at 18:35 -0700, Dan Williams wrote: > > On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska > > wrote: > > > > > > From: Jae Hyun Yoo > > > > > > ASPEED AST24xx/AST25xx/AST26xx SoCs supports the PECI electrical > > > interface (a.k.a PECI wire). > > > > Maybe a one sentence blurb here and in the Kconfig reminding people > > why they should care if they have a PECI driver or not? > > Ok, I'll expand it a bit. [..] > > > +static int aspeed_peci_xfer(struct peci_controller *controller, > > > + u8 addr, struct peci_request *req) > > > +{ > > > + struct aspeed_peci *priv = dev_get_drvdata(controller->dev.parent); > > > + unsigned long flags, timeout = msecs_to_jiffies(priv- > > > >cmd_timeout_ms); > > > + u32 peci_head; > > > + int ret; > > > + > > > + if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX || > > > + req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX) > > > + return -EINVAL; > > > + > > > + /* Check command sts and bus idle state */ > > > + ret = aspeed_peci_check_idle(priv); > > > + if (ret) > > > + return ret; /* -ETIMEDOUT */ > > > + > > > + spin_lock_irqsave(&priv->lock, flags); > > > + reinit_completion(&priv->xfer_complete); > > > + > > > + peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) | > > > + FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) | > > > + FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len); > > > + > > > + writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); > > > + > > > + memcpy_toio(priv->base + ASPEED_PECI_WR_DATA0, req->tx.buf, > > > min_t(u8, req->tx.len, 16)); > > > + if (req->tx.len > 16) > > > + memcpy_toio(priv->base + ASPEED_PECI_WR_DATA4, req->tx.buf + > > > 16, > > > + req->tx.len - 16); > > > + > > > + dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); > > > + print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, req- > > > >tx.len); > > > > On CONFIG_DYNAMIC_DEBUG=n builds the kernel will do all the work of > > reading through this buffer, but skip emitting it. Are you sure you > > want to pay that overhead for every transaction? > > I can remove it or I can add something like: > > #if IS_ENABLED(CONFIG_PECI_DEBUG) > #define peci_debug(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) > #else > #define peci_debug(...) do { } while (0) > #endif It's the hex dump I'm worried about, not the debug statements as much. I think the choices are remove the print_hex_dump_bytes(), put it behind an IS_ENABLED(CONFIG_DYNAMIC_DEBUG) to ensure the overhead is skipped in the CONFIG_DYNAMIC_DEBUG=n case, or live with the overhead if this is not a fast path / infrequently used. > > (and similar peci_trace with trace_printk for usage in IRQ handlers and such). > > What do you think? In general, no, don't wrap the base level print routines with driver-specific ones. Also, trace_printk() is only for debug builds. Note that trace points are built to be even less overhead than dev_dbg(), so there's no overhead concern with disabled tracepoints, they literally translate to nops when disabled. > > > > > > + > > > + priv->status = 0; > > > + writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); > > > + spin_unlock_irqrestore(&priv->lock, flags); > > > + > > > + ret = wait_for_completion_interruptible_timeout(&priv- > > > >xfer_complete, timeout); > > > > spin_lock_irqsave() says "I don't know if interrupts are disabled > > already, so I'll save the state, whatever it is, and restore later" > > > > wait_for_completion_interruptible_timeout() says "I know I am in a > > sleepable context where interrupts are enabled" > > > > So, one of those is wrong, i.e. should it be spin_{lock,unlock}_irq()? > > You're right - I'll fix it. > > > > > > > > + if (ret < 0) > > > + return ret; > > > + > > > + if (ret == 0) { > > > + dev_dbg(priv->dev, "Timeout waiting for a response!\n"); > > > + return -ETIMEDOUT; > > > + } > > > + > > > + spin_lock_irqsave(&priv->lock, flags); > > > + > > > + writel(0, priv->base + ASPEED_PECI_CMD); > > > + > > > + if (priv->status != ASPEED_PECI_INT_CMD_DONE) { > > > + spin_unlock_irqrestore(&priv->lock, flags); > > > + dev_dbg(priv->dev, "No valid response!\n"); > > > + return -EIO; > > > + } > > > + > > > + spin_unlock_irqrestore(&priv->lock, flags); > > > + > > > + memcpy_fromio(req->rx.buf, priv->base + ASPEED_PECI_RD_DATA0, > > > min_t(u8, req->rx.len, 16)); > > > + if (req->rx.len > 16) > > > + memcpy_fromio(req->rx.buf + 16, priv->base + > > > ASPEED_PECI_RD_DATA4, > > > + req->rx.len - 16); > > > + > > > + print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, req- > > > >rx.len); > > > + > > > + return 0; > > > +} > > > + > > > +static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) > > > +{ > > > + struct aspeed_peci *priv = arg; > > > + u32 status; > > > + > > > + spin_lock(&priv->lock); > > > + status = readl(priv->base + ASPEED_PECI_INT_STS); > > > + writel(status, priv->base + ASPEED_PECI_INT_STS); > > > + priv->status |= (status & ASPEED_PECI_INT_MASK); > > > + > > > + /* > > > + * In most cases, interrupt bits will be set one by one but also > > > note > > > + * that multiple interrupt bits could be set at the same time. > > > + */ > > > + if (status & ASPEED_PECI_INT_BUS_TIMEOUT) > > > + dev_dbg_ratelimited(priv->dev, > > > "ASPEED_PECI_INT_BUS_TIMEOUT\n"); > > > + > > > + if (status & ASPEED_PECI_INT_BUS_CONTENTION) > > > + dev_dbg_ratelimited(priv->dev, > > > "ASPEED_PECI_INT_BUS_CONTENTION\n"); > > > + > > > + if (status & ASPEED_PECI_INT_WR_FCS_BAD) > > > + dev_dbg_ratelimited(priv->dev, > > > "ASPEED_PECI_INT_WR_FCS_BAD\n"); > > > + > > > + if (status & ASPEED_PECI_INT_WR_FCS_ABORT) > > > + dev_dbg_ratelimited(priv->dev, > > > "ASPEED_PECI_INT_WR_FCS_ABORT\n"); > > > > Are you sure these would not be better as tracepoints? If you're > > debugging an interrupt related failure, the ratelimiting might get in > > your way when you really need to know when one of these error > > interrupts fire relative to another event. > > Tracepoints are ABI(ish), and using a full blown tracepoint just for IRQ status > would probably be too much. Tracepoints become ABI once someone ships tooling that depends on them being there. These don't look attractive for a tool, and they don't look difficult to maintain if the interrupt handler needs to be reworked. I.e. it would be trivial to keep a dead tracepoint around if worse came to worse to keep a tool from failing to load. > I was thinking about something like trace_printk hidden under a > "CONFIG_PECI_DEBUG" (see above), but perhaps that's something for the future > improvement? Again trace_printk() is only for private builds. > > > > > > + > > > + /* > > > + * All commands should be ended up with a ASPEED_PECI_INT_CMD_DONE > > > bit > > > + * set even in an error case. > > > + */ > > > + if (status & ASPEED_PECI_INT_CMD_DONE) > > > + complete(&priv->xfer_complete); > > > > Hmm, no need to check if there was a sequencing error, like a command > > was never submitted? > > It's handled by checking if HW is idle in xfer before a command is sent, where > we just expect a single interrupt per command. I'm asking how do you determine if this status was spurious, or there was a sequencing error in the driver? From dan.j.williams at intel.com Sat Aug 28 05:01:06 2021 From: dan.j.williams at intel.com (Dan Williams) Date: Fri, 27 Aug 2021 12:01:06 -0700 Subject: [PATCH v2 08/15] peci: Add device detection In-Reply-To: <20210803113134.2262882-9-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-9-iwona.winiarska@intel.com> Message-ID: On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska wrote: > > Since PECI devices are discoverable, we can dynamically detect devices > that are actually available in the system. > > This change complements the earlier implementation by rescanning PECI > bus to detect available devices. For this purpose, it also introduces the > minimal API for PECI requests. > > Signed-off-by: Iwona Winiarska > Reviewed-by: Pierre-Louis Bossart > --- > drivers/peci/Makefile | 2 +- > drivers/peci/core.c | 33 ++++++++++++ > drivers/peci/device.c | 114 ++++++++++++++++++++++++++++++++++++++++ > drivers/peci/internal.h | 14 +++++ > drivers/peci/request.c | 50 ++++++++++++++++++ > 5 files changed, 212 insertions(+), 1 deletion(-) > create mode 100644 drivers/peci/device.c > create mode 100644 drivers/peci/request.c > > diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile > index 926d8df15cbd..c5f9d3fe21bb 100644 > --- a/drivers/peci/Makefile > +++ b/drivers/peci/Makefile > @@ -1,7 +1,7 @@ > # SPDX-License-Identifier: GPL-2.0-only > > # Core functionality > -peci-y := core.o > +peci-y := core.o request.o device.o > obj-$(CONFIG_PECI) += peci.o > > # Hardware specific bus drivers > diff --git a/drivers/peci/core.c b/drivers/peci/core.c > index 7b3938af0396..d143f1a7fe98 100644 > --- a/drivers/peci/core.c > +++ b/drivers/peci/core.c > @@ -34,6 +34,20 @@ struct device_type peci_controller_type = { > .release = peci_controller_dev_release, > }; > > +static int peci_controller_scan_devices(struct peci_controller *controller) > +{ > + int ret; > + u8 addr; > + > + for (addr = PECI_BASE_ADDR; addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; addr++) { > + ret = peci_device_create(controller, addr); > + if (ret) > + return ret; > + } > + > + return 0; > +} > + > static struct peci_controller *peci_controller_alloc(struct device *dev, > struct peci_controller_ops *ops) > { > @@ -76,10 +90,23 @@ static struct peci_controller *peci_controller_alloc(struct device *dev, > return ERR_PTR(ret); > } > > +static int unregister_child(struct device *dev, void *dummy) > +{ > + peci_device_destroy(to_peci_device(dev)); > + > + return 0; > +} > + > static void unregister_controller(void *_controller) > { > struct peci_controller *controller = _controller; > > + /* > + * Detach any active PECI devices. This can't fail, thus we do not > + * check the returned value. > + */ > + device_for_each_child_reverse(&controller->dev, NULL, unregister_child); > + > device_unregister(&controller->dev); > } > > @@ -115,6 +142,12 @@ struct peci_controller *devm_peci_controller_add(struct device *dev, > if (ret) > return ERR_PTR(ret); > > + /* > + * Ignoring retval since failures during scan are non-critical for > + * controller itself. > + */ > + peci_controller_scan_devices(controller); > + > return controller; > > err: > diff --git a/drivers/peci/device.c b/drivers/peci/device.c > new file mode 100644 > index 000000000000..32811248997b > --- /dev/null > +++ b/drivers/peci/device.c > @@ -0,0 +1,114 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (c) 2018-2021 Intel Corporation > + > +#include > +#include > + > +#include "internal.h" > + > +static int peci_detect(struct peci_controller *controller, u8 addr) > +{ > + struct peci_request *req; > + int ret; > + > + /* > + * PECI Ping is a command encoded by tx_len = 0, rx_len = 0. > + * We expect correct Write FCS if the device at the target address > + * is able to respond. > + */ > + req = peci_request_alloc(NULL, 0, 0); > + if (!req) > + return -ENOMEM; Seems a waste to do a heap allocation for this routine. Why not: /* * PECI Ping is a command encoded by tx_len = 0, rx_len = 0. * We expect correct Write FCS if the device at the target address * is able to respond. */ struct peci_request req = { 0 }; > + > + mutex_lock(&controller->bus_lock); > + ret = controller->ops->xfer(controller, addr, req); > + mutex_unlock(&controller->bus_lock); > + > + peci_request_free(req); > + > + return ret; > +} > + > +static bool peci_addr_valid(u8 addr) > +{ > + return addr >= PECI_BASE_ADDR && addr < PECI_BASE_ADDR + PECI_DEVICE_NUM_MAX; > +} > + > +static int peci_dev_exists(struct device *dev, void *data) > +{ > + struct peci_device *device = to_peci_device(dev); > + u8 *addr = data; > + > + if (device->addr == *addr) > + return -EBUSY; > + > + return 0; > +} > + > +int peci_device_create(struct peci_controller *controller, u8 addr) > +{ > + struct peci_device *device; > + int ret; > + > + if (WARN_ON(!peci_addr_valid(addr))) The WARN_ON is overkill, especially as there is only one caller of this and it loops through valid addresses. > + return -EINVAL; > + > + /* Check if we have already detected this device before. */ > + ret = device_for_each_child(&controller->dev, &addr, peci_dev_exists); > + if (ret) > + return 0; > + > + ret = peci_detect(controller, addr); > + if (ret) { > + /* > + * Device not present or host state doesn't allow successful > + * detection at this time. > + */ > + if (ret == -EIO || ret == -ETIMEDOUT) > + return 0; > + > + return ret; > + } > + > + device = kzalloc(sizeof(*device), GFP_KERNEL); > + if (!device) > + return -ENOMEM; > + > + device->addr = addr; > + device->dev.parent = &controller->dev; > + device->dev.bus = &peci_bus_type; > + device->dev.type = &peci_device_type; > + > + ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr); > + if (ret) > + goto err_free; It's cleaner to just have one unified error exit using put_device(). Use the device_initialize() + device_add() pattern, not device_register(). > + > + ret = device_register(&device->dev); > + if (ret) > + goto err_put; > + > + return 0; > + > +err_put: > + put_device(&device->dev); > +err_free: > + kfree(device); > + > + return ret; > +} > + > +void peci_device_destroy(struct peci_device *device) > +{ > + device_unregister(&device->dev); No clear value for this wrapper, in fact in one caller it causes it to do a to_peci_device() just this helper can undo that up-cast. > +} > + > +static void peci_device_release(struct device *dev) > +{ > + struct peci_device *device = to_peci_device(dev); > + > + kfree(device); > +} > + > +struct device_type peci_device_type = { > + .release = peci_device_release, > +}; > diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h > index 918dea745a86..57d11a902c5d 100644 > --- a/drivers/peci/internal.h > +++ b/drivers/peci/internal.h > @@ -8,6 +8,20 @@ > #include > > struct peci_controller; > +struct peci_device; > +struct peci_request; > + > +/* PECI CPU address range 0x30-0x37 */ > +#define PECI_BASE_ADDR 0x30 > +#define PECI_DEVICE_NUM_MAX 8 > + > +struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len); > +void peci_request_free(struct peci_request *req); > + > +extern struct device_type peci_device_type; > + > +int peci_device_create(struct peci_controller *controller, u8 addr); > +void peci_device_destroy(struct peci_device *device); > > extern struct bus_type peci_bus_type; > > diff --git a/drivers/peci/request.c b/drivers/peci/request.c > new file mode 100644 > index 000000000000..81b567bc7b87 > --- /dev/null > +++ b/drivers/peci/request.c > @@ -0,0 +1,50 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (c) 2021 Intel Corporation > + > +#include > +#include > +#include > +#include > + > +#include "internal.h" > + > +/** > + * peci_request_alloc() - allocate &struct peci_requests > + * @device: PECI device to which request is going to be sent > + * @tx_len: TX length > + * @rx_len: RX length > + * > + * Return: A pointer to a newly allocated &struct peci_request on success or NULL otherwise. > + */ > +struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len) > +{ > + struct peci_request *req; > + > + if (WARN_ON_ONCE(tx_len > PECI_REQUEST_MAX_BUF_SIZE || rx_len > PECI_REQUEST_MAX_BUF_SIZE)) WARN_ON_ONCE() should only be here to help other kernel developers not make this mistake However, another way to enforce this is to stop exporting peci_request_alloc() and instead export helpers for specific command types, and keep this detail internal to the core. If you keep this, it needs a comment that it is only here to warn other peci-client developers of their bug before it goes upstream. From dan.j.williams at intel.com Sat Aug 28 05:11:00 2021 From: dan.j.williams at intel.com (Dan Williams) Date: Fri, 27 Aug 2021 12:11:00 -0700 Subject: [PATCH v2 09/15] peci: Add sysfs interface for PECI bus In-Reply-To: <20210803113134.2262882-10-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-10-iwona.winiarska@intel.com> Message-ID: On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska wrote: > > PECI devices may not be discoverable at the time when PECI controller is > being added (e.g. BMC can boot up when the Host system is still in S5). > Since we currently don't have the capabilities to figure out the Host > system state inside the PECI subsystem itself, we have to rely on > userspace to do it for us. > > In the future, PECI subsystem may be expanded with mechanisms that allow > us to avoid depending on userspace interaction (e.g. CPU presence could > be detected using GPIO, and the information on whether it's discoverable > could be obtained over IPMI). Thanks for this detail. > Unfortunately, those methods may ultimately not be available (support > will vary from platform to platform), which means that we still need > platform independent method triggered by userspace. > > Signed-off-by: Iwona Winiarska > --- > Documentation/ABI/testing/sysfs-bus-peci | 16 +++++ > drivers/peci/Makefile | 2 +- > drivers/peci/core.c | 3 +- > drivers/peci/device.c | 1 + > drivers/peci/internal.h | 5 ++ > drivers/peci/sysfs.c | 82 ++++++++++++++++++++++++ > 6 files changed, 107 insertions(+), 2 deletions(-) > create mode 100644 Documentation/ABI/testing/sysfs-bus-peci > create mode 100644 drivers/peci/sysfs.c > > diff --git a/Documentation/ABI/testing/sysfs-bus-peci b/Documentation/ABI/testing/sysfs-bus-peci > new file mode 100644 > index 000000000000..56c2b2216bbd > --- /dev/null > +++ b/Documentation/ABI/testing/sysfs-bus-peci > @@ -0,0 +1,16 @@ > +What: /sys/bus/peci/rescan > +Date: July 2021 > +KernelVersion: 5.15 > +Contact: Iwona Winiarska > +Description: > + Writing a non-zero value to this attribute will > + initiate scan for PECI devices on all PECI controllers > + in the system. > + > +What: /sys/bus/peci/devices/-/remove > +Date: July 2021 > +KernelVersion: 5.15 > +Contact: Iwona Winiarska > +Description: > + Writing a non-zero value to this attribute will > + remove the PECI device and any of its children. > diff --git a/drivers/peci/Makefile b/drivers/peci/Makefile > index c5f9d3fe21bb..917f689e147a 100644 > --- a/drivers/peci/Makefile > +++ b/drivers/peci/Makefile > @@ -1,7 +1,7 @@ > # SPDX-License-Identifier: GPL-2.0-only > > # Core functionality > -peci-y := core.o request.o device.o > +peci-y := core.o request.o device.o sysfs.o > obj-$(CONFIG_PECI) += peci.o > > # Hardware specific bus drivers > diff --git a/drivers/peci/core.c b/drivers/peci/core.c > index d143f1a7fe98..c473acb3c2a0 100644 > --- a/drivers/peci/core.c > +++ b/drivers/peci/core.c > @@ -34,7 +34,7 @@ struct device_type peci_controller_type = { > .release = peci_controller_dev_release, > }; > > -static int peci_controller_scan_devices(struct peci_controller *controller) > +int peci_controller_scan_devices(struct peci_controller *controller) > { > int ret; > u8 addr; > @@ -159,6 +159,7 @@ EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI); > > struct bus_type peci_bus_type = { > .name = "peci", > + .bus_groups = peci_bus_groups, > }; > > static int __init peci_init(void) > diff --git a/drivers/peci/device.c b/drivers/peci/device.c > index 32811248997b..d77d9dabd51e 100644 > --- a/drivers/peci/device.c > +++ b/drivers/peci/device.c > @@ -110,5 +110,6 @@ static void peci_device_release(struct device *dev) > } > > struct device_type peci_device_type = { > + .groups = peci_device_groups, > .release = peci_device_release, > }; > diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h > index 57d11a902c5d..978e12c8e1d3 100644 > --- a/drivers/peci/internal.h > +++ b/drivers/peci/internal.h > @@ -8,6 +8,7 @@ > #include > > struct peci_controller; > +struct attribute_group; > struct peci_device; > struct peci_request; > > @@ -19,12 +20,16 @@ struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u > void peci_request_free(struct peci_request *req); > > extern struct device_type peci_device_type; > +extern const struct attribute_group *peci_device_groups[]; > > int peci_device_create(struct peci_controller *controller, u8 addr); > void peci_device_destroy(struct peci_device *device); > > extern struct bus_type peci_bus_type; > +extern const struct attribute_group *peci_bus_groups[]; To me, sysfs.c is small enough to just fold into core.c, then no need to declare public attribute arrays like this, but up to you if you prefer the sysfs.c split. > > extern struct device_type peci_controller_type; > > +int peci_controller_scan_devices(struct peci_controller *controller); > + > #endif /* __PECI_INTERNAL_H */ > diff --git a/drivers/peci/sysfs.c b/drivers/peci/sysfs.c > new file mode 100644 > index 000000000000..db9ef05776e3 > --- /dev/null > +++ b/drivers/peci/sysfs.c > @@ -0,0 +1,82 @@ > +// SPDX-License-Identifier: GPL-2.0-only > +// Copyright (c) 2021 Intel Corporation > + > +#include > +#include > +#include > + > +#include "internal.h" > + > +static int rescan_controller(struct device *dev, void *data) > +{ > + if (dev->type != &peci_controller_type) > + return 0; > + > + return peci_controller_scan_devices(to_peci_controller(dev)); > +} > + > +static ssize_t rescan_store(struct bus_type *bus, const char *buf, size_t count) > +{ > + bool res; > + int ret; > + > + ret = kstrtobool(buf, &res); > + if (ret) > + return ret; > + > + if (!res) > + return count; > + > + ret = bus_for_each_dev(&peci_bus_type, NULL, NULL, rescan_controller); > + if (ret) > + return ret; > + > + return count; > +} > +static BUS_ATTR_WO(rescan); > + > +static struct attribute *peci_bus_attrs[] = { > + &bus_attr_rescan.attr, > + NULL > +}; > + > +static const struct attribute_group peci_bus_group = { > + .attrs = peci_bus_attrs, > +}; > + > +const struct attribute_group *peci_bus_groups[] = { > + &peci_bus_group, > + NULL > +}; > + > +static ssize_t remove_store(struct device *dev, struct device_attribute *attr, > + const char *buf, size_t count) > +{ > + struct peci_device *device = to_peci_device(dev); > + bool res; > + int ret; > + > + ret = kstrtobool(buf, &res); > + if (ret) > + return ret; > + > + if (res && device_remove_file_self(dev, attr)) > + peci_device_destroy(device); How do you solve races between sysfs device remove and controller device remove? Looks like double-free at first glance. Have a look at the kill_device() helper as one way to resolve this double-delete race.. > + > + return count; > +} > +static DEVICE_ATTR_IGNORE_LOCKDEP(remove, 0200, NULL, remove_store); > + > +static struct attribute *peci_device_attrs[] = { > + &dev_attr_remove.attr, > + NULL > +}; > + > +static const struct attribute_group peci_device_group = { > + .attrs = peci_device_attrs, > +}; > + > +const struct attribute_group *peci_device_groups[] = { > + &peci_device_group, > + NULL > +}; > -- > 2.31.1 > From dan.j.williams at intel.com Sat Aug 28 07:19:59 2021 From: dan.j.williams at intel.com (Dan Williams) Date: Fri, 27 Aug 2021 14:19:59 -0700 Subject: [PATCH v2 10/15] peci: Add support for PECI device drivers In-Reply-To: <20210803113134.2262882-11-iwona.winiarska@intel.com> References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-11-iwona.winiarska@intel.com> Message-ID: On Tue, Aug 3, 2021 at 4:36 AM Iwona Winiarska wrote: > > Here we're adding support for PECI device drivers, which unlike PECI s/Here we're adding/Add/ > controller drivers are actually able to provide functionalities to > userspace. > > We're also extending peci_request API to allow querying more details s/We're also extending/Also, extend/ ...for the most part imperative tense is the preferred tense, by upstream maintainers, for changelogs. > about PECI device (e.g. model/family), that's going to be used to find > a compatible peci_driver. > > Signed-off-by: Iwona Winiarska > Reviewed-by: Pierre-Louis Bossart > --- > drivers/peci/Kconfig | 1 + > drivers/peci/core.c | 49 +++++++++ > drivers/peci/device.c | 105 ++++++++++++++++++++ > drivers/peci/internal.h | 75 ++++++++++++++ > drivers/peci/request.c | 214 ++++++++++++++++++++++++++++++++++++++++ > include/linux/peci.h | 19 ++++ > lib/Kconfig | 2 +- > 7 files changed, 464 insertions(+), 1 deletion(-) > > diff --git a/drivers/peci/Kconfig b/drivers/peci/Kconfig > index 99279df97a78..1d0532e3a801 100644 > --- a/drivers/peci/Kconfig > +++ b/drivers/peci/Kconfig > @@ -2,6 +2,7 @@ > > menuconfig PECI > tristate "PECI support" > + select GENERIC_LIB_X86 GENERIC_LIB_X86 has dependencies, so this 'select' will make kbuild unhappy when that dependency is not met. Given that this symbol already selected by X86, it seems this just wants a "depends on GENERIC_LIB_X86". > help > The Platform Environment Control Interface (PECI) is an interface > that provides a communication channel to Intel processors and > diff --git a/drivers/peci/core.c b/drivers/peci/core.c > index c473acb3c2a0..33c07920493d 100644 > --- a/drivers/peci/core.c > +++ b/drivers/peci/core.c > @@ -157,8 +157,57 @@ struct peci_controller *devm_peci_controller_add(struct device *dev, > } > EXPORT_SYMBOL_NS_GPL(devm_peci_controller_add, PECI); > > +static const struct peci_device_id * > +peci_bus_match_device_id(const struct peci_device_id *id, struct peci_device *device) > +{ > + while (id->family != 0) { > + if (id->family == device->info.family && > + id->model == device->info.model) > + return id; > + id++; > + } > + > + return NULL; > +} > + > +static int peci_bus_device_match(struct device *dev, struct device_driver *drv) > +{ > + struct peci_device *device = to_peci_device(dev); > + struct peci_driver *peci_drv = to_peci_driver(drv); > + > + if (dev->type != &peci_device_type) > + return 0; > + > + if (peci_bus_match_device_id(peci_drv->id_table, device)) > + return 1; Save a couple lines and do: return peci_bus_match_device_id(...) > + > + return 0; > +} > + > +static int peci_bus_device_probe(struct device *dev) > +{ > + struct peci_device *device = to_peci_device(dev); > + struct peci_driver *driver = to_peci_driver(dev->driver); > + > + return driver->probe(device, peci_bus_match_device_id(driver->id_table, device)); > +} > + > +static int peci_bus_device_remove(struct device *dev) Note, in linux-next this prototype has changed to: void (*remove)(struct device *dev); https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git/tree/include/linux/device/bus.h > +{ > + struct peci_device *device = to_peci_device(dev); > + struct peci_driver *driver = to_peci_driver(dev->driver); > + > + if (driver->remove) > + driver->remove(device); > + > + return 0; > +} > + > struct bus_type peci_bus_type = { > .name = "peci", > + .match = peci_bus_device_match, > + .probe = peci_bus_device_probe, > + .remove = peci_bus_device_remove, > .bus_groups = peci_bus_groups, > }; > > diff --git a/drivers/peci/device.c b/drivers/peci/device.c > index d77d9dabd51e..a78c02399574 100644 > --- a/drivers/peci/device.c > +++ b/drivers/peci/device.c > @@ -1,11 +1,85 @@ > // SPDX-License-Identifier: GPL-2.0-only > // Copyright (c) 2018-2021 Intel Corporation > > +#include > #include > #include > +#include > > #include "internal.h" > > +#define REVISION_NUM_MASK GENMASK(15, 8) > +static int peci_get_revision(struct peci_device *device, u8 *revision) > +{ > + struct peci_request *req; > + u64 dib; > + > + req = peci_get_dib(device); I would expect peci_get_dib() to return @dib. > + if (IS_ERR(req)) > + return PTR_ERR(req); > + > + /* > + * PECI device may be in a state where it is unable to return a proper > + * DIB, in which case it returns 0 as DIB value. > + * Let's treat this as an error to avoid carrying on with the detection > + * using invalid revision. > + */ > + dib = peci_request_data_dib(req); I would expect peci_request_data_dib() to make a request. A stack allocated peci_request passed to peci_get_dib() that returns an error code would seem to be cleaner than this current organization. > + if (dib == 0) { > + peci_request_free(req); > + return -EIO; > + } > + > + *revision = FIELD_GET(REVISION_NUM_MASK, dib); > + > + peci_request_free(req); > + > + return 0; > +} > + > +static int peci_get_cpu_id(struct peci_device *device, u32 *cpu_id) > +{ > + struct peci_request *req; > + int ret; > + > + req = peci_pkg_cfg_readl(device, PECI_PCS_PKG_ID, PECI_PKG_ID_CPU_ID); > + if (IS_ERR(req)) > + return PTR_ERR(req); > + > + ret = peci_request_status(req); > + if (ret) > + goto out_req_free; > + > + *cpu_id = peci_request_data_readl(req); > +out_req_free: > + peci_request_free(req); > + > + return ret; > +} > + > +static int peci_device_info_init(struct peci_device *device) > +{ > + u8 revision; > + u32 cpu_id; > + int ret; > + > + ret = peci_get_cpu_id(device, &cpu_id); > + if (ret) > + return ret; > + > + device->info.family = x86_family(cpu_id); > + device->info.model = x86_model(cpu_id); > + > + ret = peci_get_revision(device, &revision); > + if (ret) > + return ret; > + device->info.peci_revision = revision; > + > + device->info.socket_id = device->addr - PECI_BASE_ADDR; > + > + return 0; > +} > + > static int peci_detect(struct peci_controller *controller, u8 addr) > { > struct peci_request *req; > @@ -79,6 +153,10 @@ int peci_device_create(struct peci_controller *controller, u8 addr) > device->dev.bus = &peci_bus_type; > device->dev.type = &peci_device_type; > > + ret = peci_device_info_init(device); > + if (ret) > + goto err_free; > + > ret = dev_set_name(&device->dev, "%d-%02x", controller->id, device->addr); > if (ret) > goto err_free; > @@ -102,6 +180,33 @@ void peci_device_destroy(struct peci_device *device) > device_unregister(&device->dev); > } > > +int __peci_driver_register(struct peci_driver *driver, struct module *owner, > + const char *mod_name) > +{ > + driver->driver.bus = &peci_bus_type; > + driver->driver.owner = owner; > + driver->driver.mod_name = mod_name; > + > + if (!driver->probe) { > + pr_err("peci: trying to register driver without probe callback\n"); > + return -EINVAL; > + } > + > + if (!driver->id_table) { > + pr_err("peci: trying to register driver without device id table\n"); > + return -EINVAL; > + } > + > + return driver_register(&driver->driver); > +} > +EXPORT_SYMBOL_NS_GPL(__peci_driver_register, PECI); > + > +void peci_driver_unregister(struct peci_driver *driver) > +{ > + driver_unregister(&driver->driver); > +} > +EXPORT_SYMBOL_NS_GPL(peci_driver_unregister, PECI); > + > static void peci_device_release(struct device *dev) > { > struct peci_device *device = to_peci_device(dev); > diff --git a/drivers/peci/internal.h b/drivers/peci/internal.h > index 978e12c8e1d3..d661e1b65694 100644 > --- a/drivers/peci/internal.h > +++ b/drivers/peci/internal.h > @@ -19,6 +19,34 @@ struct peci_request; > struct peci_request *peci_request_alloc(struct peci_device *device, u8 tx_len, u8 rx_len); > void peci_request_free(struct peci_request *req); > > +int peci_request_status(struct peci_request *req); > +u64 peci_request_data_dib(struct peci_request *req); > + > +u8 peci_request_data_readb(struct peci_request *req); > +u16 peci_request_data_readw(struct peci_request *req); > +u32 peci_request_data_readl(struct peci_request *req); > +u64 peci_request_data_readq(struct peci_request *req); > + > +struct peci_request *peci_get_dib(struct peci_device *device); > +struct peci_request *peci_get_temp(struct peci_device *device); > + > +struct peci_request *peci_pkg_cfg_readb(struct peci_device *device, u8 index, u16 param); > +struct peci_request *peci_pkg_cfg_readw(struct peci_device *device, u8 index, u16 param); > +struct peci_request *peci_pkg_cfg_readl(struct peci_device *device, u8 index, u16 param); > +struct peci_request *peci_pkg_cfg_readq(struct peci_device *device, u8 index, u16 param); > + > +/** > + * struct peci_device_id - PECI device data to match > + * @data: pointer to driver private data specific to device > + * @family: device family > + * @model: device model > + */ > +struct peci_device_id { > + const void *data; > + u16 family; > + u8 model; > +}; > + > extern struct device_type peci_device_type; > extern const struct attribute_group *peci_device_groups[]; > > @@ -28,6 +56,53 @@ void peci_device_destroy(struct peci_device *device); > extern struct bus_type peci_bus_type; > extern const struct attribute_group *peci_bus_groups[]; > > +/** > + * struct peci_driver - PECI driver > + * @driver: inherit device driver > + * @probe: probe callback > + * @remove: remove callback > + * @id_table: PECI device match table to decide which device to bind > + */ > +struct peci_driver { > + struct device_driver driver; > + int (*probe)(struct peci_device *device, const struct peci_device_id *id); > + void (*remove)(struct peci_device *device); > + const struct peci_device_id *id_table; > +}; > + > +static inline struct peci_driver *to_peci_driver(struct device_driver *d) > +{ > + return container_of(d, struct peci_driver, driver); > +} > + > +int __peci_driver_register(struct peci_driver *driver, struct module *owner, > + const char *mod_name); > +/** > + * peci_driver_register() - register PECI driver > + * @driver: the driver to be registered > + * @owner: owner module of the driver being registered > + * @mod_name: module name string > + * > + * PECI drivers that don't need to do anything special in module init should > + * use the convenience "module_peci_driver" macro instead > + * > + * Return: zero on success, else a negative error code. > + */ > +#define peci_driver_register(driver) \ > + __peci_driver_register(driver, THIS_MODULE, KBUILD_MODNAME) > +void peci_driver_unregister(struct peci_driver *driver); > + > +/** > + * module_peci_driver() - helper macro for registering a modular PECI driver > + * @__peci_driver: peci_driver struct > + * > + * Helper macro for PECI drivers which do not do anything special in module > + * init/exit. This eliminates a lot of boilerplate. Each module may only > + * use this macro once, and calling it replaces module_init() and module_exit() > + */ > +#define module_peci_driver(__peci_driver) \ > + module_driver(__peci_driver, peci_driver_register, peci_driver_unregister) > + > extern struct device_type peci_controller_type; > > int peci_controller_scan_devices(struct peci_controller *controller); > diff --git a/drivers/peci/request.c b/drivers/peci/request.c > index 81b567bc7b87..fe032d5a5e1b 100644 > --- a/drivers/peci/request.c > +++ b/drivers/peci/request.c > @@ -1,13 +1,140 @@ > // SPDX-License-Identifier: GPL-2.0-only > // Copyright (c) 2021 Intel Corporation > > +#include > #include > #include > #include > #include > > +#include > + > #include "internal.h" > > +#define PECI_GET_DIB_CMD 0xf7 > +#define PECI_GET_DIB_WR_LEN 1 > +#define PECI_GET_DIB_RD_LEN 8 > + > +#define PECI_RDPKGCFG_CMD 0xa1 > +#define PECI_RDPKGCFG_WR_LEN 5 > +#define PECI_RDPKGCFG_RD_LEN_BASE 1 > +#define PECI_WRPKGCFG_CMD 0xa5 > +#define PECI_WRPKGCFG_WR_LEN_BASE 6 > +#define PECI_WRPKGCFG_RD_LEN 1 > + > +/* Device Specific Completion Code (CC) Definition */ > +#define PECI_CC_SUCCESS 0x40 > +#define PECI_CC_NEED_RETRY 0x80 > +#define PECI_CC_OUT_OF_RESOURCE 0x81 > +#define PECI_CC_UNAVAIL_RESOURCE 0x82 > +#define PECI_CC_INVALID_REQ 0x90 > +#define PECI_CC_MCA_ERROR 0x91 > +#define PECI_CC_CATASTROPHIC_MCA_ERROR 0x93 > +#define PECI_CC_FATAL_MCA_ERROR 0x94 > +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB 0x98 > +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR 0x9B > +#define PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA 0x9C > + > +#define PECI_RETRY_BIT BIT(0) > + > +#define PECI_RETRY_TIMEOUT msecs_to_jiffies(700) > +#define PECI_RETRY_INTERVAL_MIN msecs_to_jiffies(1) > +#define PECI_RETRY_INTERVAL_MAX msecs_to_jiffies(128) > + > +static u8 peci_request_data_cc(struct peci_request *req) > +{ > + return req->rx.buf[0]; > +} > + > +/** > + * peci_request_status() - return -errno based on PECI completion code > + * @req: the PECI request that contains response data with completion code > + * > + * It can't be used for Ping(), GetDIB() and GetTemp() - for those commands we > + * don't expect completion code in the response. > + * > + * Return: -errno > + */ > +int peci_request_status(struct peci_request *req) > +{ > + u8 cc = peci_request_data_cc(req); > + > + if (cc != PECI_CC_SUCCESS) > + dev_dbg(&req->device->dev, "ret: %#02x\n", cc); > + > + switch (cc) { > + case PECI_CC_SUCCESS: > + return 0; > + case PECI_CC_NEED_RETRY: > + case PECI_CC_OUT_OF_RESOURCE: > + case PECI_CC_UNAVAIL_RESOURCE: > + return -EAGAIN; > + case PECI_CC_INVALID_REQ: > + return -EINVAL; > + case PECI_CC_MCA_ERROR: > + case PECI_CC_CATASTROPHIC_MCA_ERROR: > + case PECI_CC_FATAL_MCA_ERROR: > + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB: > + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_IERR: > + case PECI_CC_PARITY_ERR_GPSB_OR_PMSB_MCA: > + return -EIO; > + } > + > + WARN_ONCE(1, "Unknown PECI completion code: %#02x\n", cc); > + > + return -EIO; > +} > +EXPORT_SYMBOL_NS_GPL(peci_request_status, PECI); > + > +static int peci_request_xfer(struct peci_request *req) > +{ > + struct peci_device *device = req->device; > + struct peci_controller *controller = to_peci_controller(device->dev.parent); > + int ret; > + > + mutex_lock(&controller->bus_lock); > + ret = controller->ops->xfer(controller, device->addr, req); > + mutex_unlock(&controller->bus_lock); > + > + return ret; > +} > + > +static int peci_request_xfer_retry(struct peci_request *req) > +{ > + long wait_interval = PECI_RETRY_INTERVAL_MIN; > + struct peci_device *device = req->device; > + struct peci_controller *controller = to_peci_controller(device->dev.parent); > + unsigned long start = jiffies; > + int ret; > + > + /* Don't try to use it for ping */ > + if (WARN_ON(!req->rx.buf)) > + return 0; > + > + do { > + ret = peci_request_xfer(req); > + if (ret) { > + dev_dbg(&controller->dev, "xfer error: %d\n", ret); > + return ret; > + } > + > + if (peci_request_status(req) != -EAGAIN) > + return 0; > + > + /* Set the retry bit to indicate a retry attempt */ > + req->tx.buf[1] |= PECI_RETRY_BIT; > + > + if (schedule_timeout_interruptible(wait_interval)) > + return -ERESTARTSYS; > + > + wait_interval = min_t(long, wait_interval * 2, PECI_RETRY_INTERVAL_MAX); > + } while (time_before(jiffies, start + PECI_RETRY_TIMEOUT)); > + > + dev_dbg(&controller->dev, "request timed out\n"); > + > + return -ETIMEDOUT; > +} > + > /** > * peci_request_alloc() - allocate &struct peci_requests > * @device: PECI device to which request is going to be sent > @@ -48,3 +175,90 @@ void peci_request_free(struct peci_request *req) > kfree(req); > } > EXPORT_SYMBOL_NS_GPL(peci_request_free, PECI); > + > +struct peci_request *peci_get_dib(struct peci_device *device) > +{ > + struct peci_request *req; > + int ret; > + > + req = peci_request_alloc(device, PECI_GET_DIB_WR_LEN, PECI_GET_DIB_RD_LEN); > + if (!req) > + return ERR_PTR(-ENOMEM); > + > + req->tx.buf[0] = PECI_GET_DIB_CMD; > + > + ret = peci_request_xfer(req); > + if (ret) { > + peci_request_free(req); > + return ERR_PTR(ret); > + } > + > + return req; > +} > +EXPORT_SYMBOL_NS_GPL(peci_get_dib, PECI); > + > +static struct peci_request * > +__pkg_cfg_read(struct peci_device *device, u8 index, u16 param, u8 len) > +{ > + struct peci_request *req; > + int ret; > + > + req = peci_request_alloc(device, PECI_RDPKGCFG_WR_LEN, PECI_RDPKGCFG_RD_LEN_BASE + len); > + if (!req) > + return ERR_PTR(-ENOMEM); > + > + req->tx.buf[0] = PECI_RDPKGCFG_CMD; > + req->tx.buf[1] = 0; > + req->tx.buf[2] = index; > + put_unaligned_le16(param, &req->tx.buf[3]); > + > + ret = peci_request_xfer_retry(req); > + if (ret) { > + peci_request_free(req); > + return ERR_PTR(ret); > + } > + > + return req; > +} > + > +u8 peci_request_data_readb(struct peci_request *req) > +{ > + return req->rx.buf[1]; > +} > +EXPORT_SYMBOL_NS_GPL(peci_request_data_readb, PECI); > + > +u16 peci_request_data_readw(struct peci_request *req) > +{ > + return get_unaligned_le16(&req->rx.buf[1]); > +} > +EXPORT_SYMBOL_NS_GPL(peci_request_data_readw, PECI); > + > +u32 peci_request_data_readl(struct peci_request *req) > +{ > + return get_unaligned_le32(&req->rx.buf[1]); > +} > +EXPORT_SYMBOL_NS_GPL(peci_request_data_readl, PECI); > + > +u64 peci_request_data_readq(struct peci_request *req) > +{ > + return get_unaligned_le64(&req->rx.buf[1]); > +} > +EXPORT_SYMBOL_NS_GPL(peci_request_data_readq, PECI); > + > +u64 peci_request_data_dib(struct peci_request *req) > +{ > + return get_unaligned_le64(&req->rx.buf[0]); > +} > +EXPORT_SYMBOL_NS_GPL(peci_request_data_dib, PECI); > + > +#define __read_pkg_config(x, type) \ > +struct peci_request *peci_pkg_cfg_##x(struct peci_device *device, u8 index, u16 param) \ > +{ \ > + return __pkg_cfg_read(device, index, param, sizeof(type)); \ > +} \ > +EXPORT_SYMBOL_NS_GPL(peci_pkg_cfg_##x, PECI) > + > +__read_pkg_config(readb, u8); > +__read_pkg_config(readw, u16); > +__read_pkg_config(readl, u32); > +__read_pkg_config(readq, u64); > diff --git a/include/linux/peci.h b/include/linux/peci.h > index 26e0a4e73b50..dcf1c53f4e40 100644 > --- a/include/linux/peci.h > +++ b/include/linux/peci.h > @@ -14,6 +14,14 @@ > */ > #define PECI_REQUEST_MAX_BUF_SIZE 32 > > +#define PECI_PCS_PKG_ID 0 /* Package Identifier Read */ > +#define PECI_PKG_ID_CPU_ID 0x0000 /* CPUID Info */ > +#define PECI_PKG_ID_PLATFORM_ID 0x0001 /* Platform ID */ > +#define PECI_PKG_ID_DEVICE_ID 0x0002 /* Uncore Device ID */ > +#define PECI_PKG_ID_MAX_THREAD_ID 0x0003 /* Max Thread ID */ > +#define PECI_PKG_ID_MICROCODE_REV 0x0004 /* CPU Microcode Update Revision */ > +#define PECI_PKG_ID_MCA_ERROR_LOG 0x0005 /* Machine Check Status */ > + > struct peci_controller; > struct peci_request; > > @@ -59,6 +67,11 @@ static inline struct peci_controller *to_peci_controller(void *d) > * struct peci_device - PECI device > * @dev: device object to register PECI device to the device model > * @controller: manages the bus segment hosting this PECI device > + * @info: PECI device characteristics > + * @info.family: device family > + * @info.model: device model > + * @info.peci_revision: PECI revision supported by the PECI device > + * @info.socket_id: the socket ID represented by the PECI device > * @addr: address used on the PECI bus connected to the parent controller > * > * A peci_device identifies a single device (i.e. CPU) connected to a PECI bus. > @@ -67,6 +80,12 @@ static inline struct peci_controller *to_peci_controller(void *d) > */ > struct peci_device { > struct device dev; > + struct { > + u16 family; > + u8 model; > + u8 peci_revision; > + u8 socket_id; > + } info; > u8 addr; > }; > > diff --git a/lib/Kconfig b/lib/Kconfig > index e538d4d773bd..7f7972d357c2 100644 > --- a/lib/Kconfig > +++ b/lib/Kconfig > @@ -718,4 +718,4 @@ config ASN1_ENCODER > > config GENERIC_LIB_X86 > bool > - depends on X86 > + depends on X86 || PECI This looks broken, what in the GENERIC_LIB_X86 implementation depends on peci? From jic23 at kernel.org Mon Aug 30 00:53:44 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 15:53:44 +0100 Subject: [v4 01/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: References: <20210823070240.12600-1-billy_tsai@aspeedtech.com> <20210823070240.12600-2-billy_tsai@aspeedtech.com> Message-ID: <20210829155344.370348d1@jic23-huawei> On Wed, 25 Aug 2021 00:06:47 +0000 Billy Tsai wrote: > Hi Rob, > > On 2021/8/24, 8:13 PM, "Rob Herring" wrote: > > On Mon, Aug 23, 2021 at 03:02:26PM +0800, Billy Tsai wrote: > > > +properties: > > > + compatible: > > > + enum: > > > + - aspeed,ast2600-adc0 > > > + - aspeed,ast2600-adc1 > > > What's the difference between 0 and 1? > > Their trimming data, which is used to calibrate internal reference volage, > locates in different address of OTP. At very least document that with a description: here to avoid anyone looking at this later asking the same question! Jonathan > > Best Regards, > Billy Tsai > > > From jic23 at kernel.org Mon Aug 30 01:07:08 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:07:08 +0100 Subject: [RESEND v4 02/15] iio: adc: aspeed: completes the bitfield declare. In-Reply-To: <20210824091243.9393-3-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <20210824091243.9393-3-billy_tsai@aspeedtech.com> Message-ID: <20210829160708.2b1a26c0@jic23-huawei> On Tue, 24 Aug 2021 17:12:30 +0800 Billy Tsai wrote: > This patch completes the declare of adc register bitfields and uses the > same prefix ASPEED_ADC_* for these bitfields. > > Signed-off-by: Billy Tsai > --- > drivers/iio/adc/aspeed_adc.c | 56 +++++++++++++++++++++++++----------- > 1 file changed, 39 insertions(+), 17 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 19efaa41bc34..7010d56ac3b9 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -3,6 +3,7 @@ > * Aspeed AST2400/2500 ADC > * > * Copyright (C) 2017 Google, Inc. > + * Copyright (C) 2021 Aspeed Technology Inc. > */ > > #include > @@ -16,6 +17,7 @@ > #include > #include > #include > +#include > > #include > #include > @@ -28,15 +30,31 @@ > #define ASPEED_REG_INTERRUPT_CONTROL 0x04 > #define ASPEED_REG_VGA_DETECT_CONTROL 0x08 > #define ASPEED_REG_CLOCK_CONTROL 0x0C > -#define ASPEED_REG_MAX 0xC0 > - > -#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) > -#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) > -#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) > - > -#define ASPEED_ENGINE_ENABLE BIT(0) > - > -#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) > +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 > +#define ASPEED_REG_MAX 0xCC This REG_MAX value is used as a sanity check for debugfs based access. As this raises the limit for all devices supported by the driver, not just the new ast2600, are we fine to read these non existent registers on the other parts? If so, perhaps a comment to say that here somewhere? > + > +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) Are all the following in the same register? Bit usual to have overlapping fields, so if they are, perhaps a few comments to explain what is going on would be good. > +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) > +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 > +#define ASPEED_ADC_OP_MODE_STANDBY 1 > +#define ASPEED_ADC_OP_MODE_NORMAL 7 > +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) > +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) > +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) > +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 > +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 > +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 > +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 > +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) > +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 > +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 > +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) > +#define ASPEED_ADC_CH7_MODE BIT(12) > +#define ASPEED_ADC_CH7_NORMAL 0 > +#define ASPEED_ADC_CH7_BAT 1 > +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) > +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) > +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) > > #define ASPEED_ADC_INIT_POLLING_TIME 500 > #define ASPEED_ADC_INIT_TIMEOUT 500000 > @@ -226,7 +244,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) > > if (model_data->wait_init_sequence) { > /* Enable engine in normal mode. */ > - writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, > + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, > + ASPEED_ADC_OP_MODE_NORMAL) | > + ASPEED_ADC_ENGINE_ENABLE, > data->base + ASPEED_REG_ENGINE_CONTROL); > > /* Wait for initial sequence complete. */ > @@ -245,10 +265,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) > if (ret) > goto clk_enable_error; > > - adc_engine_control_reg_val = GENMASK(31, 16) | > - ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; > + adc_engine_control_reg_val = > + ASPEED_ADC_CTRL_CHANNEL | > + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | > + ASPEED_ADC_ENGINE_ENABLE; > writel(adc_engine_control_reg_val, > - data->base + ASPEED_REG_ENGINE_CONTROL); > + data->base + ASPEED_REG_ENGINE_CONTROL); > > model_data = of_device_get_match_data(&pdev->dev); > indio_dev->name = model_data->model_name; > @@ -264,8 +286,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) > return 0; > > iio_register_error: > - writel(ASPEED_OPERATION_MODE_POWER_DOWN, > - data->base + ASPEED_REG_ENGINE_CONTROL); > + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), > + data->base + ASPEED_REG_ENGINE_CONTROL); > clk_disable_unprepare(data->clk_scaler->clk); > clk_enable_error: > poll_timeout_error: > @@ -283,8 +305,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) > struct aspeed_adc_data *data = iio_priv(indio_dev); > > iio_device_unregister(indio_dev); > - writel(ASPEED_OPERATION_MODE_POWER_DOWN, > - data->base + ASPEED_REG_ENGINE_CONTROL); > + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), > + data->base + ASPEED_REG_ENGINE_CONTROL); Ideally these white space changes would be in a precursor patch. At the very least mention them in the patch description. They are good thing to tidy up, but they do add noise to this patch. > clk_disable_unprepare(data->clk_scaler->clk); > reset_control_assert(data->rst); > clk_hw_unregister_divider(data->clk_scaler); From jic23 at kernel.org Mon Aug 30 01:08:56 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:08:56 +0100 Subject: [RESEND v4 03/15] iio: adc: aspeed: set driver data when adc probe. In-Reply-To: <20210824091243.9393-4-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <20210824091243.9393-4-billy_tsai@aspeedtech.com> Message-ID: <20210829160856.12dd9395@jic23-huawei> On Tue, 24 Aug 2021 17:12:31 +0800 Billy Tsai wrote: > Fix the issue when adc remove will get the null driver data. > > Signed-off-by: Billy Tsai This is a fix that we should probably backport. Please add a fixes tag and move it to the start of the series so I can pick it during this kernel cycle rather than waiting for the next one. Thanks, Jonathan > --- > drivers/iio/adc/aspeed_adc.c | 1 + > 1 file changed, 1 insertion(+) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 7010d56ac3b9..20462cf659e4 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -201,6 +201,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) > > data = iio_priv(indio_dev); > data->dev = &pdev->dev; > + platform_set_drvdata(pdev, indio_dev); > > data->base = devm_platform_ioremap_resource(pdev, 0); > if (IS_ERR(data->base)) From jic23 at kernel.org Mon Aug 30 01:13:18 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:13:18 +0100 Subject: [RESEND v4 05/15] iio: adc: aspeed: Refactory model data structure In-Reply-To: <20210824091243.9393-6-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <20210824091243.9393-6-billy_tsai@aspeedtech.com> Message-ID: <20210829161318.54997151@jic23-huawei> On Tue, 24 Aug 2021 17:12:33 +0800 Billy Tsai wrote: > This patch refactory the model data structure to distinguish the > function form differnet version of aspeed adc. from different versions > - Rename the vref_voltag to vref_fixed and add vref driver data vref_voltage > When driver probe will check vref_fixed value and store it > to vref which isn't const value. > - Add num_channels > Make num_channles of iio device can be changed by differnet model_data different > - Add need_prescaler flag and scaler_bit_width > The need_prescaler flag used to tell the driver the clock divider needs > another prescaler and the scaler_bit_width to set the clock divider > bitfiled width. bitfield? > > Signed-off-by: Billy Tsai > --- > drivers/iio/adc/aspeed_adc.c | 18 ++++++++++++++---- > 1 file changed, 14 insertions(+), 4 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index d85aa31ee3b1..f03c7921d534 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -63,8 +63,11 @@ struct aspeed_adc_model_data { > const char *model_name; > unsigned int min_sampling_rate; // Hz > unsigned int max_sampling_rate; // Hz > - unsigned int vref_voltage; // mV > + unsigned int vref_fixed; // mV Whilst here, you could make the name carry the information of the units. e.g. vref_fixed_mv; That way we always know what the units are without having to spot this comment. > bool wait_init_sequence; > + bool need_prescaler; > + u8 scaler_bit_width; > + unsigned int num_channels; > }; > > struct aspeed_adc_data { > @@ -75,6 +78,7 @@ struct aspeed_adc_data { > struct clk_hw *clk_prescaler; > struct clk_hw *clk_scaler; > struct reset_control *rst; > + int vref; vref_mv as above perhaps? > }; > > #define ASPEED_CHAN(_idx, _data_reg_addr) { \ > @@ -118,7 +122,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, > return IIO_VAL_INT; > > case IIO_CHAN_INFO_SCALE: > - *val = data->model_data->vref_voltage; > + *val = data->model_data->vref_fixed; > *val2 = ASPEED_RESOLUTION_BITS; > return IIO_VAL_FRACTIONAL_LOG2; > > @@ -312,17 +316,23 @@ static int aspeed_adc_remove(struct platform_device *pdev) > > static const struct aspeed_adc_model_data ast2400_model_data = { > .model_name = "ast2400-adc", > - .vref_voltage = 2500, // mV > + .vref_fixed = 2500, // mV > .min_sampling_rate = 10000, > .max_sampling_rate = 500000, > + .need_prescaler = true, > + .scaler_bit_width = 10, > + .num_channels = 16, > }; > > static const struct aspeed_adc_model_data ast2500_model_data = { > .model_name = "ast2500-adc", > - .vref_voltage = 1800, // mV > + .vref_fixed = 1800, // mV > .min_sampling_rate = 1, > .max_sampling_rate = 1000000, > .wait_init_sequence = true, > + .need_prescaler = true, > + .scaler_bit_width = 10, > + .num_channels = 16, > }; > > static const struct of_device_id aspeed_adc_matches[] = { From jic23 at kernel.org Mon Aug 30 01:20:22 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:20:22 +0100 Subject: [RESEND v4 08/15] iio: adc: aspeed: Use model_data to set clk scaler. In-Reply-To: <202108250006.17P06IgG097015@twspam01.aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250006.17P06IgG097015@twspam01.aspeedtech.com> Message-ID: <20210829162022.7ee18854@jic23-huawei> On Tue, 24 Aug 2021 17:12:36 +0800 Billy Tsai wrote: > This patch use need_prescaler and scaler_bit_width to set the adc clock > scaler. > > Signed-off-by: Billy Tsai Hi Billy, One minor comment inline. Thanks, Jonathan > --- > drivers/iio/adc/aspeed_adc.c | 39 +++++++++++++++++++++--------------- > 1 file changed, 23 insertions(+), 16 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 2d6215a91f99..52db38be9699 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -202,9 +202,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) > { > struct iio_dev *indio_dev; > struct aspeed_adc_data *data; > - const char *clk_parent_name; > int ret; > u32 adc_engine_control_reg_val; > + unsigned long scaler_flags = 0; > + char clk_name[32], clk_parent_name[32]; > > indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); > if (!indio_dev) > @@ -221,24 +222,28 @@ static int aspeed_adc_probe(struct platform_device *pdev) > > /* Register ADC clock prescaler with source specified by device tree. */ > spin_lock_init(&data->clk_lock); > - clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); > - > - data->clk_prescaler = clk_hw_register_divider( > - &pdev->dev, "prescaler", clk_parent_name, 0, > - data->base + ASPEED_REG_CLOCK_CONTROL, > - 17, 15, 0, &data->clk_lock); > - if (IS_ERR(data->clk_prescaler)) > - return PTR_ERR(data->clk_prescaler); > - > + snprintf(clk_parent_name, 32, of_clk_get_parent_name(pdev->dev.of_node, 0)); ARRAY_SIZE(clk_parent_name) instead of 32. Same for other places this pattern occurs. > + if (data->model_data->need_prescaler) { > + snprintf(clk_name, 32, "%s-prescaler", > + data->model_data->model_name); > + data->clk_prescaler = clk_hw_register_divider( > + &pdev->dev, clk_name, clk_parent_name, 0, > + data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, > + &data->clk_lock); > + if (IS_ERR(data->clk_prescaler)) > + return PTR_ERR(data->clk_prescaler); > + snprintf(clk_parent_name, 32, clk_name); > + scaler_flags = CLK_SET_RATE_PARENT; > + } > /* > * Register ADC clock scaler downstream from the prescaler. Allow rate > * setting to adjust the prescaler as well. > */ > + snprintf(clk_name, 32, "%s-scaler", data->model_data->model_name); > data->clk_scaler = clk_hw_register_divider( > - &pdev->dev, "scaler", "prescaler", > - CLK_SET_RATE_PARENT, > - data->base + ASPEED_REG_CLOCK_CONTROL, > - 0, 10, 0, &data->clk_lock); > + &pdev->dev, clk_name, clk_parent_name, scaler_flags, > + data->base + ASPEED_REG_CLOCK_CONTROL, 0, > + data->model_data->scaler_bit_width, 0, &data->clk_lock); > if (IS_ERR(data->clk_scaler)) { > ret = PTR_ERR(data->clk_scaler); > goto scaler_error; > @@ -310,7 +315,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) > reset_error: > clk_hw_unregister_divider(data->clk_scaler); > scaler_error: > - clk_hw_unregister_divider(data->clk_prescaler); > + if (data->model_data->need_prescaler) > + clk_hw_unregister_divider(data->clk_prescaler); > return ret; > } > > @@ -325,7 +331,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) > clk_disable_unprepare(data->clk_scaler->clk); > reset_control_assert(data->rst); > clk_hw_unregister_divider(data->clk_scaler); > - clk_hw_unregister_divider(data->clk_prescaler); > + if (data->model_data->need_prescaler) > + clk_hw_unregister_divider(data->clk_prescaler); > > return 0; > } From jic23 at kernel.org Mon Aug 30 01:25:48 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:25:48 +0100 Subject: [RESEND v4 09/15] iio: adc: aspeed: Use devm_add_action_or_reset. In-Reply-To: <202108250004.17P04FdD094082@twspam01.aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250004.17P04FdD094082@twspam01.aspeedtech.com> Message-ID: <20210829162548.40aa9f91@jic23-huawei> On Tue, 24 Aug 2021 17:12:37 +0800 Billy Tsai wrote: > This patch use devm_add_action_or_reset to handle the error in probe > phase. > > Signed-off-by: Billy Tsai > --- > drivers/iio/adc/aspeed_adc.c | 92 +++++++++++++++++++++--------------- > 1 file changed, 55 insertions(+), 37 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 52db38be9699..1c87e12a0cab 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -187,6 +187,27 @@ static const struct iio_info aspeed_adc_iio_info = { > .debugfs_reg_access = aspeed_adc_reg_access, > }; > > +static void aspeed_adc_unregister_divider(void *data) > +{ > + struct clk_hw *clk = data; > + > + clk_hw_unregister_divider(clk); > +} > + > +static void aspeed_adc_reset_assert(void *data) > +{ > + struct reset_control *rst = data; > + > + reset_control_assert(rst); > +} > + > +static void aspeed_adc_clk_disable_unprepare(void *data) > +{ > + struct clk *clk = data; > + > + clk_disable_unprepare(clk); > +} > + > static int aspeed_adc_vref_config(struct iio_dev *indio_dev) > { > struct aspeed_adc_data *data = iio_priv(indio_dev); > @@ -232,6 +253,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) > &data->clk_lock); > if (IS_ERR(data->clk_prescaler)) > return PTR_ERR(data->clk_prescaler); > + > + ret = devm_add_action_or_reset(data->dev, > + aspeed_adc_unregister_divider, > + data->clk_prescaler); > + if (ret) > + return ret; > snprintf(clk_parent_name, 32, clk_name); > scaler_flags = CLK_SET_RATE_PARENT; > } > @@ -244,23 +271,30 @@ static int aspeed_adc_probe(struct platform_device *pdev) > &pdev->dev, clk_name, clk_parent_name, scaler_flags, > data->base + ASPEED_REG_CLOCK_CONTROL, 0, > data->model_data->scaler_bit_width, 0, &data->clk_lock); > - if (IS_ERR(data->clk_scaler)) { > - ret = PTR_ERR(data->clk_scaler); > - goto scaler_error; > - } > + if (IS_ERR(data->clk_scaler)) > + return PTR_ERR(data->clk_scaler); > + > + ret = devm_add_action_or_reset(data->dev, aspeed_adc_unregister_divider, > + data->clk_scaler); > + if (ret) > + return ret; > > data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); > if (IS_ERR(data->rst)) { > dev_err(&pdev->dev, > "invalid or missing reset controller device tree entry"); > - ret = PTR_ERR(data->rst); > - goto reset_error; > + return PTR_ERR(data->rst); > } > reset_control_deassert(data->rst); > > + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, > + data->rst); > + if (ret) > + return ret; > + > ret = aspeed_adc_vref_config(indio_dev); > if (ret) > - goto vref_config_error; > + return ret; > > if (data->model_data->wait_init_sequence) { > /* Enable engine in normal mode. */ > @@ -277,13 +311,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) > ASPEED_ADC_INIT_POLLING_TIME, > ASPEED_ADC_INIT_TIMEOUT); > if (ret) > - goto poll_timeout_error; > + return ret; > } > > /* Start all channels in normal mode. */ > ret = clk_prepare_enable(data->clk_scaler->clk); > if (ret) > - goto clk_enable_error; > + return ret; > + > + ret = devm_add_action_or_reset(data->dev, > + aspeed_adc_clk_disable_unprepare, > + data->clk_scaler->clk); > + if (ret) > + return ret; > > adc_engine_control_reg_val = > ASPEED_ADC_CTRL_CHANNEL | > @@ -299,41 +339,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) > indio_dev->num_channels = data->model_data->num_channels; > > ret = iio_device_register(indio_dev); > - if (ret) > - goto iio_register_error; > - > + if (ret) { > + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, > + ASPEED_ADC_OP_MODE_PWR_DOWN), > + data->base + ASPEED_REG_ENGINE_CONTROL); > + return ret; > + } > return 0; > - > -iio_register_error: > - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), > - data->base + ASPEED_REG_ENGINE_CONTROL); > - clk_disable_unprepare(data->clk_scaler->clk); > -clk_enable_error: > -poll_timeout_error: > -vref_config_error: > - reset_control_assert(data->rst); > -reset_error: > - clk_hw_unregister_divider(data->clk_scaler); > -scaler_error: > - if (data->model_data->need_prescaler) > - clk_hw_unregister_divider(data->clk_prescaler); > - return ret; > } > > static int aspeed_adc_remove(struct platform_device *pdev) > { > struct iio_dev *indio_dev = platform_get_drvdata(pdev); > - struct aspeed_adc_data *data = iio_priv(indio_dev); > - > iio_device_unregister(indio_dev); Given there is no longer anything here, you should be safe to call devm_iio_device_unregister() and get rid of this function entirely. > - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), > - data->base + ASPEED_REG_ENGINE_CONTROL); Unless I'm missing something this is not handled via any of the devm_add_action_or_reset() callbacks. You handle it in the error paths manually but if you move this to managed as well you can drop that handling and still have it automatically called on remove. > - clk_disable_unprepare(data->clk_scaler->clk); > - reset_control_assert(data->rst); > - clk_hw_unregister_divider(data->clk_scaler); > - if (data->model_data->need_prescaler) > - clk_hw_unregister_divider(data->clk_prescaler); > - > return 0; > } > From jic23 at kernel.org Mon Aug 30 01:31:37 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:31:37 +0100 Subject: [RESEND v4 10/15] iio: adc: aspeed: Support ast2600 adc. In-Reply-To: <20210824091243.9393-11-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <20210824091243.9393-11-billy_tsai@aspeedtech.com> Message-ID: <20210829163137.4c0ce7b0@jic23-huawei> On Tue, 24 Aug 2021 17:12:38 +0800 Billy Tsai wrote: > Make driver to support ast2600 adc device. > - Use shared reset controller > - Complete the vref configure function > - Add the model data for ast2600 adc > > Signed-off-by: Billy Tsai Hi Billy, A few minor things inline, Jonathan > --- > drivers/iio/adc/aspeed_adc.c | 106 +++++++++++++++++++++++++++++++++-- > 1 file changed, 100 insertions(+), 6 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 1c87e12a0cab..ea3e9a52fcc9 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -1,6 +1,6 @@ > // SPDX-License-Identifier: GPL-2.0-only > /* > - * Aspeed AST2400/2500 ADC > + * Aspeed AST2400/2500/2600 ADC > * > * Copyright (C) 2017 Google, Inc. > * Copyright (C) 2021 Aspeed Technology Inc. > @@ -14,6 +14,7 @@ > #include > #include > #include > +#include > #include > #include > #include > @@ -73,6 +74,7 @@ struct aspeed_adc_model_data { > struct aspeed_adc_data { > struct device *dev; > const struct aspeed_adc_model_data *model_data; > + struct regulator *regulator; > void __iomem *base; > spinlock_t clk_lock; > struct clk_hw *clk_prescaler; > @@ -208,14 +210,80 @@ static void aspeed_adc_clk_disable_unprepare(void *data) > clk_disable_unprepare(clk); > } > > +static void aspeed_adc_reg_disable(void *data) > +{ > + struct regulator *reg = data; > + > + regulator_disable(reg); > +} > + > static int aspeed_adc_vref_config(struct iio_dev *indio_dev) > { > struct aspeed_adc_data *data = iio_priv(indio_dev); > + int ret; > + u32 adc_engine_control_reg_val = > + readl(data->base + ASPEED_REG_ENGINE_CONTROL); Given you aren't going to use this if vref_fixed is set, move the read until after that. > > if (data->model_data->vref_fixed) { > data->vref = data->model_data->vref_fixed; > return 0; > } > + > + data->regulator = devm_regulator_get_optional(data->dev, "vref"); > + if (!IS_ERR(data->regulator)) { > + ret = regulator_enable(data->regulator); > + if (ret) > + return ret; > + ret = devm_add_action_or_reset( > + data->dev, aspeed_adc_reg_disable, data->regulator); > + if (ret) > + return ret; > + data->vref = regulator_get_voltage(data->regulator); > + /* Conversion from uV to mV */ > + data->vref /= 1000; > + if ((data->vref >= 1550) && (data->vref <= 2700)) > + writel(adc_engine_control_reg_val | > + FIELD_PREP( > + ASPEED_ADC_REF_VOLTAGE, > + ASPEED_ADC_REF_VOLTAGE_EXT_HIGH), > + data->base + ASPEED_REG_ENGINE_CONTROL); > + else if ((data->vref >= 900) && (data->vref <= 1650)) > + writel(adc_engine_control_reg_val | > + FIELD_PREP( > + ASPEED_ADC_REF_VOLTAGE, > + ASPEED_ADC_REF_VOLTAGE_EXT_LOW), > + data->base + ASPEED_REG_ENGINE_CONTROL); > + else { > + dev_err(data->dev, "Regulator voltage %d not support", > + data->vref); > + return -EOPNOTSUPP; > + } > + } else { > + if (PTR_ERR(data->regulator) != -ENODEV) > + return PTR_ERR(data->regulator); > + ret = of_property_read_u32(data->dev->of_node, > + "aspeed,int_vref_mv", &data->vref); > + if (ret < 0) { > + dev_warn(data->dev, > + "Using default vref: internal 2500 mv"); Perhaps document that as default in the dt-binding, then you can remove this warning and handle this as: data->vref = 2500; of_property_read_u32(data->dev->of_node, "aspeed,int-vref-mvolts", &data->vref); etc > + data->vref = 2500; > + } > + if (data->vref == 2500) > + writel(adc_engine_control_reg_val | > + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, > + ASPEED_ADC writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL);_REF_VOLTAGE_2500mV), > + data->base + ASPEED_REG_ENGINE_CONTROL); > + else if (data->vref == 1200) > + writel(adc_engine_control_reg_val | > + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, > + ASPEED_ADC_REF_VOLTAGE_1200mV), > + data->base + ASPEED_REG_ENGINE_CONTROL); > + else { > + dev_err(data->dev, "Voltage %d not support", data->vref); > + return -EOPNOTSUPP; > + } > + } > + > return 0; > } > > @@ -279,7 +347,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) > if (ret) > return ret; > > - data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); > + data->rst = devm_reset_control_get_shared(&pdev->dev, NULL); > if (IS_ERR(data->rst)) { > dev_err(&pdev->dev, > "invalid or missing reset controller device tree entry"); > @@ -297,10 +365,14 @@ static int aspeed_adc_probe(struct platform_device *pdev) > return ret; > > if (data->model_data->wait_init_sequence) { > + adc_engine_control_reg_val = > + readl(data->base + ASPEED_REG_ENGINE_CONTROL); > + adc_engine_control_reg_val |= > + FIELD_PREP(ASPEED_ADC_OP_MODE, > + ASPEED_ADC_OP_MODE_NORMAL) | > + ASPEED_ADC_ENGINE_ENABLE; > /* Enable engine in normal mode. */ > - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, > - ASPEED_ADC_OP_MODE_NORMAL) | > - ASPEED_ADC_ENGINE_ENABLE, > + writel(adc_engine_control_reg_val, > data->base + ASPEED_REG_ENGINE_CONTROL); > > /* Wait for initial sequence complete. */ > @@ -326,6 +398,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) > return ret; > > adc_engine_control_reg_val = > + readl(data->base + ASPEED_REG_ENGINE_CONTROL); > + adc_engine_control_reg_val |= > ASPEED_ADC_CTRL_CHANNEL | > FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | > ASPEED_ADC_ENGINE_ENABLE; > @@ -376,9 +450,29 @@ static const struct aspeed_adc_model_data ast2500_model_data = { > .num_channels = 16, > }; > > +static const struct aspeed_adc_model_data ast2600_adc0_model_data = { > + .model_name = "ast2600-adc0", > + .min_sampling_rate = 10000, > + .max_sampling_rate = 500000, > + .wait_init_sequence = true, > + .scaler_bit_width = 16, > + .num_channels = 8, > +}; > + > +static const struct aspeed_adc_model_data ast2600_adc1_model_data = { > + .model_name = "ast2600-adc1", > + .min_sampling_rate = 10000, > + .max_sampling_rate = 500000, > + .wait_init_sequence = true, > + .scaler_bit_width = 16, > + .num_channels = 8, > +}; > + > static const struct of_device_id aspeed_adc_matches[] = { > { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, > { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, > + { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, > + { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, > {}, > }; > MODULE_DEVICE_TABLE(of, aspeed_adc_matches); > @@ -395,5 +489,5 @@ static struct platform_driver aspeed_adc_driver = { > module_platform_driver(aspeed_adc_driver); > > MODULE_AUTHOR("Rick Altherr "); > -MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); > +MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver"); > MODULE_LICENSE("GPL"); From jic23 at kernel.org Mon Aug 30 01:33:46 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:33:46 +0100 Subject: [RESEND v4 11/15] iio: adc: aspeed: Fix the calculate error of clock. In-Reply-To: <20210824091243.9393-12-billy_tsai@aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <20210824091243.9393-12-billy_tsai@aspeedtech.com> Message-ID: <20210829163346.501fdb43@jic23-huawei> On Tue, 24 Aug 2021 17:12:39 +0800 Billy Tsai wrote: > The adc clcok formula is clock > ast2400/2500: > ADC clock period = PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) > ast2600: > ADC clock period = PCLK * 2 * (ADC0C[15:0] + 1) > They all have one fixed divided 2 and the legacy driver didn't handle it. > This patch register the fixed factory clock device as the parent of adc > clock scaler to fix this issue. What are the impacts of this being wrong before? Is this something we should look to backport? Comment inline. > > Signed-off-by: Billy Tsai > --- > drivers/iio/adc/aspeed_adc.c | 26 ++++++++++++++++++++++++++ > 1 file changed, 26 insertions(+) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index ea3e9a52fcc9..8fe7da1a651f 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -4,6 +4,12 @@ > * > * Copyright (C) 2017 Google, Inc. > * Copyright (C) 2021 Aspeed Technology Inc. > + * > + * ADC clock formula: > + * Ast2400/Ast2500: > + * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) > + * Ast2600: > + * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1) > */ > > #include > @@ -77,6 +83,7 @@ struct aspeed_adc_data { > struct regulator *regulator; > void __iomem *base; > spinlock_t clk_lock; > + struct clk_hw *fixed_div_clk; > struct clk_hw *clk_prescaler; > struct clk_hw *clk_scaler; > struct reset_control *rst; > @@ -196,6 +203,13 @@ static void aspeed_adc_unregister_divider(void *data) > clk_hw_unregister_divider(clk); > } > > +static void aspeed_adc_unregister_fixed_divider(void *data) > +{ > + struct clk_hw *clk = data; > + > + clk_hw_unregister_fixed_factor(clk); > +} > + > static void aspeed_adc_reset_assert(void *data) > { > struct reset_control *rst = data; > @@ -312,6 +326,18 @@ static int aspeed_adc_probe(struct platform_device *pdev) > /* Register ADC clock prescaler with source specified by device tree. */ > spin_lock_init(&data->clk_lock); > snprintf(clk_parent_name, 32, of_clk_get_parent_name(pdev->dev.of_node, 0)); > + snprintf(clk_name, 32, "%s-fixed-div", data->model_data->model_name); ARRAY_SIZE > + data->fixed_div_clk = clk_hw_register_fixed_factor( > + &pdev->dev, clk_name, clk_parent_name, 0, 1, 2); > + if (IS_ERR(data->fixed_div_clk)) > + return PTR_ERR(data->fixed_div_clk); > + > + ret = devm_add_action_or_reset(data->dev, > + aspeed_adc_unregister_fixed_divider, > + data->clk_prescaler); > + if (ret) > + return ret; > + snprintf(clk_parent_name, 32, clk_name); > if (data->model_data->need_prescaler) { > snprintf(clk_name, 32, "%s-prescaler", > data->model_data->model_name); From jic23 at kernel.org Mon Aug 30 01:36:59 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:36:59 +0100 Subject: [RESEND v4 12/15] iio: adc: aspeed: Add func to set sampling rate. In-Reply-To: <202108250003.17P03KRU092474@twspam01.aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250003.17P03KRU092474@twspam01.aspeedtech.com> Message-ID: <20210829163659.2f7f5974@jic23-huawei> On Tue, 24 Aug 2021 17:12:40 +0800 Billy Tsai wrote: > Add the function to set the sampling rate and keep the sampling period > for a driver used to wait the lastest value. > > Signed-off-by: Billy Tsai Why move the code as well as factoring out the setter function? I doubt it does any harm, but I'd like to understand why you did it. Jonathan > --- > drivers/iio/adc/aspeed_adc.c | 58 +++++++++++++++++++++++++----------- > 1 file changed, 40 insertions(+), 18 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 8fe7da1a651f..4d979dd7fe88 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -65,6 +65,12 @@ > > #define ASPEED_ADC_INIT_POLLING_TIME 500 > #define ASPEED_ADC_INIT_TIMEOUT 500000 > +/* > + * When the sampling rate is too high, the ADC may not have enough charging > + * time, resulting in a low voltage value. Thus, default use slow sampling > + * rate for most user case. > + */ > +#define ASPEED_ADC_DEF_SAMPLING_RATE 65000 > > struct aspeed_adc_model_data { > const char *model_name; > @@ -88,6 +94,7 @@ struct aspeed_adc_data { > struct clk_hw *clk_scaler; > struct reset_control *rst; > int vref; > + u32 sample_period_ns; > }; > > #define ASPEED_CHAN(_idx, _data_reg_addr) { \ > @@ -119,6 +126,24 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { > ASPEED_CHAN(15, 0x2E), > }; > > +static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) > +{ > + struct aspeed_adc_data *data = iio_priv(indio_dev); > + > + if (rate < data->model_data->min_sampling_rate || > + rate > data->model_data->max_sampling_rate) > + return -EINVAL; > + /* Each sampling needs 12 clocks to covert.*/ convert. Please run a spell checker over these patches. > + clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE); > + rate = clk_get_rate(data->clk_scaler->clk); > + data->sample_period_ns = DIV_ROUND_UP_ULL( > + (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate); > + dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate, > + data->sample_period_ns); > + > + return 0; > +} > + > static int aspeed_adc_read_raw(struct iio_dev *indio_dev, > struct iio_chan_spec const *chan, > int *val, int *val2, long mask) > @@ -149,17 +174,10 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, > struct iio_chan_spec const *chan, > int val, int val2, long mask) > { > - struct aspeed_adc_data *data = iio_priv(indio_dev); > > switch (mask) { > case IIO_CHAN_INFO_SAMP_FREQ: > - if (val < data->model_data->min_sampling_rate || > - val > data->model_data->max_sampling_rate) > - return -EINVAL; > - > - clk_set_rate(data->clk_scaler->clk, > - val * ASPEED_CLOCKS_PER_SAMPLE); > - return 0; > + return aspeed_adc_set_sampling_rate(indio_dev, val); > > case IIO_CHAN_INFO_SCALE: > case IIO_CHAN_INFO_RAW: > @@ -386,6 +404,20 @@ static int aspeed_adc_probe(struct platform_device *pdev) > if (ret) > return ret; > > + ret = clk_prepare_enable(data->clk_scaler->clk); > + if (ret) > + return ret; > + > + ret = devm_add_action_or_reset(data->dev, > + aspeed_adc_clk_disable_unprepare, > + data->clk_scaler->clk); > + if (ret) > + return ret; > + > + ret = aspeed_adc_set_sampling_rate(indio_dev, ASPEED_ADC_DEF_SAMPLING_RATE); > + if (ret) > + return ret; > + > ret = aspeed_adc_vref_config(indio_dev); > if (ret) > return ret; > @@ -413,16 +445,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) > } > > /* Start all channels in normal mode. */ Why move this code up? > - ret = clk_prepare_enable(data->clk_scaler->clk); > - if (ret) > - return ret; > - > - ret = devm_add_action_or_reset(data->dev, > - aspeed_adc_clk_disable_unprepare, > - data->clk_scaler->clk); > - if (ret) > - return ret; > - > adc_engine_control_reg_val = > readl(data->base + ASPEED_REG_ENGINE_CONTROL); > adc_engine_control_reg_val |= From jic23 at kernel.org Mon Aug 30 01:39:23 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:39:23 +0100 Subject: [RESEND v4 13/15] iio: adc: aspeed: Add compensation phase. In-Reply-To: <202108250006.17P066YP096721@twspam01.aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250006.17P066YP096721@twspam01.aspeedtech.com> Message-ID: <20210829163923.107f4027@jic23-huawei> On Tue, 24 Aug 2021 17:12:41 +0800 Billy Tsai wrote: > This patch adds a compensation phase to improve the accurate of ADC accuracy of the ADC measurement. > measurement. This is the built-in function though input half of the > reference voltage to get the ADC offset. > > Signed-off-by: Billy Tsai This looks like fairly standard calibration which is good to have. Thanks, Jonathan > --- > drivers/iio/adc/aspeed_adc.c | 54 +++++++++++++++++++++++++++++++++++- > 1 file changed, 53 insertions(+), 1 deletion(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 4d979dd7fe88..20caf28dff18 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -95,6 +95,7 @@ struct aspeed_adc_data { > struct reset_control *rst; > int vref; > u32 sample_period_ns; > + int cv; > }; > > #define ASPEED_CHAN(_idx, _data_reg_addr) { \ > @@ -104,7 +105,8 @@ struct aspeed_adc_data { > .address = (_data_reg_addr), \ > .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ > - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ > + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ > + BIT(IIO_CHAN_INFO_OFFSET), \ > } > > static const struct iio_chan_spec aspeed_adc_iio_channels[] = { > @@ -126,6 +128,51 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { > ASPEED_CHAN(15, 0x2E), > }; > > +static int aspeed_adc_compensation(struct iio_dev *indio_dev) > +{ > + struct aspeed_adc_data *data = iio_priv(indio_dev); > + u32 index, adc_raw = 0; > + u32 adc_engine_control_reg_val; > + > + adc_engine_control_reg_val = > + readl(data->base + ASPEED_REG_ENGINE_CONTROL); > + adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE; > + adc_engine_control_reg_val |= > + (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | > + ASPEED_ADC_ENGINE_ENABLE); > + /* > + * Enable compensating sensing: > + * After that, the input voltage of ADC will force to half of the reference > + * voltage. So the expected reading raw data will become half of the max > + * value. We can get compensating value = 0x200 - ADC read raw value. > + * It is recommended to average at least 10 samples to get a final CV. > + */ > + writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION | > + ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), > + data->base + ASPEED_REG_ENGINE_CONTROL); > + /* > + * After enable compensating sensing mode need to wait some time for ADC stable > + * Experiment result is 1ms. > + */ > + mdelay(1); > + > + for (index = 0; index < 16; index++) { > + /* > + * Waiting for the sampling period ensures that the value acquired > + * is fresh each time. > + */ > + ndelay(data->sample_period_ns); > + adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address); > + } > + adc_raw >>= 4; > + data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw; > + writel(adc_engine_control_reg_val, > + data->base + ASPEED_REG_ENGINE_CONTROL); > + dev_dbg(data->dev, "Compensating value = %d\n", data->cv); > + > + return 0; > +} > + > static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) > { > struct aspeed_adc_data *data = iio_priv(indio_dev); > @@ -155,6 +202,10 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, > *val = readw(data->base + chan->address); > return IIO_VAL_INT; > > + case IIO_CHAN_INFO_OFFSET: > + *val = data->cv; > + return IIO_VAL_INT; > + > case IIO_CHAN_INFO_SCALE: > *val = data->vref; > *val2 = ASPEED_RESOLUTION_BITS; > @@ -444,6 +495,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) > return ret; > } > > + aspeed_adc_compensation(indio_dev); > /* Start all channels in normal mode. */ > adc_engine_control_reg_val = > readl(data->base + ASPEED_REG_ENGINE_CONTROL); From jic23 at kernel.org Mon Aug 30 01:43:38 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:43:38 +0100 Subject: [RESEND v4 14/15] iio: adc: aspeed: Support battery sensing. In-Reply-To: <202108250005.17P05agj096445@twspam01.aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250005.17P05agj096445@twspam01.aspeedtech.com> Message-ID: <20210829164338.2c18decd@jic23-huawei> On Tue, 24 Aug 2021 17:12:42 +0800 Billy Tsai wrote: > In ast2600, ADC integrate dividing circuit at last input channel for > battery sensing. This patch use the dts property "battery-sensing" to > enable this feature makes the last channel of each adc can tolerance > higher voltage than reference voltage. > > Signed-off-by: Billy Tsai The slight issue here is this changes the userspace ABI for the older parts. Whilst a per channel offset is perfectly valid it's still an ABI change and someone might be relying on a dodgy script that uses it. So, whilst it's a pain you should have two different chan_spec arrays and pick between them dependent on model of device. That way you can leave the old ABI untouched and add this control for the ast2600 only. > --- > drivers/iio/adc/aspeed_adc.c | 62 +++++++++++++++++++++++++++++++++--- > 1 file changed, 57 insertions(+), 5 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 20caf28dff18..0c5d84e82561 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -79,10 +79,16 @@ struct aspeed_adc_model_data { > unsigned int vref_fixed; // mV > bool wait_init_sequence; > bool need_prescaler; > + bool bat_sense_sup; > u8 scaler_bit_width; > unsigned int num_channels; > }; > > +struct adc_gain { > + u8 mult; > + u8 div; > +}; > + > struct aspeed_adc_data { > struct device *dev; > const struct aspeed_adc_model_data *model_data; > @@ -96,6 +102,8 @@ struct aspeed_adc_data { > int vref; > u32 sample_period_ns; > int cv; > + bool battery_sensing; > + struct adc_gain battery_mode_gain; > }; > > #define ASPEED_CHAN(_idx, _data_reg_addr) { \ > @@ -103,10 +111,10 @@ struct aspeed_adc_data { > .indexed = 1, \ > .channel = (_idx), \ > .address = (_data_reg_addr), \ > - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ > - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ > - BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ > + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ > BIT(IIO_CHAN_INFO_OFFSET), \ > + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ > + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ > } > > static const struct iio_chan_spec aspeed_adc_iio_channels[] = { > @@ -196,14 +204,39 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, > int *val, int *val2, long mask) > { > struct aspeed_adc_data *data = iio_priv(indio_dev); > + u32 adc_engine_control_reg_val; > > switch (mask) { > case IIO_CHAN_INFO_RAW: > - *val = readw(data->base + chan->address); > + if (data->battery_sensing && chan->channel == 7) { > + adc_engine_control_reg_val = > + readl(data->base + ASPEED_REG_ENGINE_CONTROL); > + writel(adc_engine_control_reg_val | > + FIELD_PREP(ASPEED_ADC_CH7_MODE, > + ASPEED_ADC_CH7_BAT) | > + ASPEED_ADC_BAT_SENSING_ENABLE, > + data->base + ASPEED_REG_ENGINE_CONTROL); > + /* > + * After enable battery sensing mode need to wait some time for adc stable > + * Experiment result is 1ms. > + */ > + mdelay(1); > + *val = readw(data->base + chan->address); > + *val = (*val * data->battery_mode_gain.mult) / > + data->battery_mode_gain.div; > + /* Restore control register value */ > + writel(adc_engine_control_reg_val, > + data->base + ASPEED_REG_ENGINE_CONTROL); > + } else > + *val = readw(data->base + chan->address); > return IIO_VAL_INT; > > case IIO_CHAN_INFO_OFFSET: > - *val = data->cv; > + if (data->battery_sensing && chan->channel == 7) > + *val = (data->cv * data->battery_mode_gain.mult) / > + data->battery_mode_gain.div; > + else > + *val = data->cv; > return IIO_VAL_INT; > > case IIO_CHAN_INFO_SCALE: > @@ -473,6 +506,23 @@ static int aspeed_adc_probe(struct platform_device *pdev) > if (ret) > return ret; > > + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", > + NULL)) { > + if (data->model_data->bat_sense_sup) { > + data->battery_sensing = 1; > + if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) & > + ASPEED_ADC_BAT_SENSING_DIV) { > + data->battery_mode_gain.mult = 3; > + data->battery_mode_gain.div = 1; > + } else { > + data->battery_mode_gain.mult = 3; > + data->battery_mode_gain.div = 2; > + } > + } else > + dev_warn(&pdev->dev, > + "Failed to enable battey-sensing mode\n"); > + } > + > if (data->model_data->wait_init_sequence) { > adc_engine_control_reg_val = > readl(data->base + ASPEED_REG_ENGINE_CONTROL); > @@ -555,6 +605,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { > .min_sampling_rate = 10000, > .max_sampling_rate = 500000, > .wait_init_sequence = true, > + .bat_sense_sup = true, > .scaler_bit_width = 16, > .num_channels = 8, > }; > @@ -564,6 +615,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { > .min_sampling_rate = 10000, > .max_sampling_rate = 500000, > .wait_init_sequence = true, > + .bat_sense_sup = true, > .scaler_bit_width = 16, > .num_channels = 8, > }; From jic23 at kernel.org Mon Aug 30 01:45:31 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Sun, 29 Aug 2021 16:45:31 +0100 Subject: [RESEND v4 15/15] iio: adc: aspeed: Get and set trimming data. In-Reply-To: <202108250007.17P07NFj097422@twspam01.aspeedtech.com> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250007.17P07NFj097422@twspam01.aspeedtech.com> Message-ID: <20210829164531.44cbcc21@jic23-huawei> On Tue, 24 Aug 2021 17:12:43 +0800 Billy Tsai wrote: > The adc controller have trimming register for fine-tune the reference Nice to have ADC instead of adc (though not that important as meaning is clear). > voltage. The trimming value come from the otp register which will be > written before chip product. This patch will read this otp value and written during chip production? (perhaps that's what is intended?) > configure it to the adc register when adc controller probe and using dts > property "aspeed,trim-data-valid" to determine whether to execute this > flow. > > Signed-off-by: Billy Tsai > --- > drivers/iio/adc/aspeed_adc.c | 68 ++++++++++++++++++++++++++++++++++++ > 1 file changed, 68 insertions(+) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index 0c5d84e82561..bd7fb23f3510 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c > @@ -25,6 +25,8 @@ > #include > #include > #include > +#include > +#include > > #include > #include > @@ -72,6 +74,11 @@ > */ > #define ASPEED_ADC_DEF_SAMPLING_RATE 65000 > > +struct aspeed_adc_trim_locate { > + const unsigned int offset; > + const unsigned int field; > +}; > + > struct aspeed_adc_model_data { > const char *model_name; > unsigned int min_sampling_rate; // Hz > @@ -82,6 +89,7 @@ struct aspeed_adc_model_data { > bool bat_sense_sup; > u8 scaler_bit_width; > unsigned int num_channels; > + const struct aspeed_adc_trim_locate *trim_locate; > }; > > struct adc_gain { > @@ -136,6 +144,44 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { > ASPEED_CHAN(15, 0x2E), > }; > > +static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev) > +{ > + struct device_node *syscon; > + struct regmap *scu; > + u32 scu_otp, trimming_val; > + struct aspeed_adc_data *data = iio_priv(indio_dev); > + > + syscon = of_find_node_by_name(NULL, "syscon"); > + if (syscon == NULL) { > + dev_warn(data->dev, "Couldn't find syscon node\n"); > + return -EOPNOTSUPP; > + } > + scu = syscon_node_to_regmap(syscon); > + if (IS_ERR(scu)) { > + dev_warn(data->dev, "Failed to get syscon regmap\n"); > + return -EOPNOTSUPP; > + } > + if (data->model_data->trim_locate) { > + if (regmap_read(scu, data->model_data->trim_locate->offset, > + &scu_otp)) { > + dev_warn(data->dev, > + "Failed to get adc trimming data\n"); > + trimming_val = 0x8; > + } else { > + trimming_val = > + ((scu_otp) & > + (data->model_data->trim_locate->field)) >> > + __ffs(data->model_data->trim_locate->field); > + } > + dev_dbg(data->dev, > + "trimming val = %d, offset = %08x, fields = %08x\n", > + trimming_val, data->model_data->trim_locate->offset, > + data->model_data->trim_locate->field); > + writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM); > + } > + return 0; > +} > + > static int aspeed_adc_compensation(struct iio_dev *indio_dev) > { > struct aspeed_adc_data *data = iio_priv(indio_dev); > @@ -506,6 +552,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) > if (ret) > return ret; > > + if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid", > + NULL)) > + aspeed_adc_set_trim_data(indio_dev); > + > if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", > NULL)) { > if (data->model_data->bat_sense_sup) { > @@ -579,6 +629,21 @@ static int aspeed_adc_remove(struct platform_device *pdev) > return 0; > } > > +static const struct aspeed_adc_trim_locate ast2500_adc_trim = { > + .offset = 0x154, > + .field = GENMASK(31, 28), > +}; > + > +static const struct aspeed_adc_trim_locate ast2600_adc0_trim = { > + .offset = 0x5d0, > + .field = GENMASK(3, 0), > +}; > + > +static const struct aspeed_adc_trim_locate ast2600_adc1_trim = { > + .offset = 0x5d0, > + .field = GENMASK(7, 4), > +}; > + > static const struct aspeed_adc_model_data ast2400_model_data = { > .model_name = "ast2400-adc", > .vref_fixed = 2500, // mV > @@ -598,6 +663,7 @@ static const struct aspeed_adc_model_data ast2500_model_data = { > .need_prescaler = true, > .scaler_bit_width = 10, > .num_channels = 16, > + .trim_locate = &ast2500_adc_trim, > }; > > static const struct aspeed_adc_model_data ast2600_adc0_model_data = { > @@ -608,6 +674,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { > .bat_sense_sup = true, > .scaler_bit_width = 16, > .num_channels = 8, > + .trim_locate = &ast2600_adc0_trim, > }; > > static const struct aspeed_adc_model_data ast2600_adc1_model_data = { > @@ -618,6 +685,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { > .bat_sense_sup = true, > .scaler_bit_width = 16, > .num_channels = 8, > + .trim_locate = &ast2600_adc1_trim, > }; > > static const struct of_device_id aspeed_adc_matches[] = { From iwona.winiarska at intel.com Mon Aug 30 05:42:09 2021 From: iwona.winiarska at intel.com (Winiarska, Iwona) Date: Sun, 29 Aug 2021 19:42:09 +0000 Subject: [PATCH v2 07/15] peci: Add peci-aspeed controller driver In-Reply-To: References: <20210803113134.2262882-1-iwona.winiarska@intel.com> <20210803113134.2262882-8-iwona.winiarska@intel.com> Message-ID: On Fri, 2021-08-27 at 09:24 -0700, Dan Williams wrote: > On Thu, Aug 26, 2021 at 4:55 PM Winiarska, Iwona > wrote: > > > > On Wed, 2021-08-25 at 18:35 -0700, Dan Williams wrote: > > > On Tue, Aug 3, 2021 at 4:35 AM Iwona Winiarska > > > wrote: > > > > > > > > From: Jae Hyun Yoo > > > > > > > > ASPEED AST24xx/AST25xx/AST26xx SoCs supports the PECI electrical > > > > interface (a.k.a PECI wire). > > > > > > Maybe a one sentence blurb here and in the Kconfig reminding people > > > why they should care if they have a PECI driver or not? > > > > Ok, I'll expand it a bit. > [..] > > > > +static int aspeed_peci_xfer(struct peci_controller *controller, > > > > +?????????????????????????? u8 addr, struct peci_request *req) > > > > +{ > > > > +?????? struct aspeed_peci *priv = dev_get_drvdata(controller- > > > > >dev.parent); > > > > +?????? unsigned long flags, timeout = msecs_to_jiffies(priv- > > > > > cmd_timeout_ms); > > > > +?????? u32 peci_head; > > > > +?????? int ret; > > > > + > > > > +?????? if (req->tx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX || > > > > +?????????? req->rx.len > ASPEED_PECI_DATA_BUF_SIZE_MAX) > > > > +?????????????? return -EINVAL; > > > > + > > > > +?????? /* Check command sts and bus idle state */ > > > > +?????? ret = aspeed_peci_check_idle(priv); > > > > +?????? if (ret) > > > > +?????????????? return ret; /* -ETIMEDOUT */ > > > > + > > > > +?????? spin_lock_irqsave(&priv->lock, flags); > > > > +?????? reinit_completion(&priv->xfer_complete); > > > > + > > > > +?????? peci_head = FIELD_PREP(ASPEED_PECI_TARGET_ADDR_MASK, addr) | > > > > +?????????????????? FIELD_PREP(ASPEED_PECI_WR_LEN_MASK, req->tx.len) | > > > > +?????????????????? FIELD_PREP(ASPEED_PECI_RD_LEN_MASK, req->rx.len); > > > > + > > > > +?????? writel(peci_head, priv->base + ASPEED_PECI_RW_LENGTH); > > > > + > > > > +?????? memcpy_toio(priv->base + ASPEED_PECI_WR_DATA0, req->tx.buf, > > > > min_t(u8, req->tx.len, 16)); > > > > +?????? if (req->tx.len > 16) > > > > +?????????????? memcpy_toio(priv->base + ASPEED_PECI_WR_DATA4, req- > > > > >tx.buf + > > > > 16, > > > > +?????????????????????????? req->tx.len - 16); > > > > + > > > > +?????? dev_dbg(priv->dev, "HEAD : 0x%08x\n", peci_head); > > > > +?????? print_hex_dump_bytes("TX : ", DUMP_PREFIX_NONE, req->tx.buf, > > > > req- > > > > > tx.len); > > > > > > On CONFIG_DYNAMIC_DEBUG=n builds the kernel will do all the work of > > > reading through this buffer, but skip emitting it. Are you sure you > > > want to pay that overhead for every transaction? > > > > I can remove it or I can add something like: > > > > #if IS_ENABLED(CONFIG_PECI_DEBUG) > > #define peci_debug(fmt, ...) pr_debug(fmt, ##__VA_ARGS__) > > #else > > #define peci_debug(...) do { } while (0) > > #endif > > It's the hex dump I'm worried about, not the debug statements as much. > > I think the choices are remove the print_hex_dump_bytes(), put it > behind an IS_ENABLED(CONFIG_DYNAMIC_DEBUG) to ensure the overhead is > skipped in the CONFIG_DYNAMIC_DEBUG=n case, or live with the overhead > if this is not a fast path / infrequently used. I will place it behind IS_ENABLED(CONFIG_DYNAMIC_DEBUG). > > > > > (and similar peci_trace with trace_printk for usage in IRQ handlers and > > such). > > > > What do you think? > > In general, no, don't wrap the base level print routines with > driver-specific ones. Also, trace_printk() is only for debug builds. > Note that trace points are built to be even less overhead than > dev_dbg(), so there's no overhead concern with disabled tracepoints, > they literally translate to nops when disabled. Ack. > > > > > > > > > > + > > > > +?????? priv->status = 0; > > > > +?????? writel(ASPEED_PECI_CMD_FIRE, priv->base + ASPEED_PECI_CMD); > > > > +?????? spin_unlock_irqrestore(&priv->lock, flags); > > > > + > > > > +?????? ret = wait_for_completion_interruptible_timeout(&priv- > > > > > xfer_complete, timeout); > > > > > > spin_lock_irqsave() says "I don't know if interrupts are disabled > > > already, so I'll save the state, whatever it is, and restore later" > > > > > > wait_for_completion_interruptible_timeout() says "I know I am in a > > > sleepable context where interrupts are enabled" > > > > > > So, one of those is wrong, i.e. should it be spin_{lock,unlock}_irq()? > > > > You're right - I'll fix it. > > > > > > > > > > > > +?????? if (ret < 0) > > > > +?????????????? return ret; > > > > + > > > > +?????? if (ret == 0) { > > > > +?????????????? dev_dbg(priv->dev, "Timeout waiting for a response!\n"); > > > > +?????????????? return -ETIMEDOUT; > > > > +?????? } > > > > + > > > > +?????? spin_lock_irqsave(&priv->lock, flags); > > > > + > > > > +?????? writel(0, priv->base + ASPEED_PECI_CMD); > > > > + > > > > +?????? if (priv->status != ASPEED_PECI_INT_CMD_DONE) { > > > > +?????????????? spin_unlock_irqrestore(&priv->lock, flags); > > > > +?????????????? dev_dbg(priv->dev, "No valid response!\n"); > > > > +?????????????? return -EIO; > > > > +?????? } > > > > + > > > > +?????? spin_unlock_irqrestore(&priv->lock, flags); > > > > + > > > > +?????? memcpy_fromio(req->rx.buf, priv->base + ASPEED_PECI_RD_DATA0, > > > > min_t(u8, req->rx.len, 16)); > > > > +?????? if (req->rx.len > 16) > > > > +?????????????? memcpy_fromio(req->rx.buf + 16, priv->base + > > > > ASPEED_PECI_RD_DATA4, > > > > +???????????????????????????? req->rx.len - 16); > > > > + > > > > +?????? print_hex_dump_bytes("RX : ", DUMP_PREFIX_NONE, req->rx.buf, > > > > req- > > > > > rx.len); > > > > + > > > > +?????? return 0; > > > > +} > > > > + > > > > +static irqreturn_t aspeed_peci_irq_handler(int irq, void *arg) > > > > +{ > > > > +?????? struct aspeed_peci *priv = arg; > > > > +?????? u32 status; > > > > + > > > > +?????? spin_lock(&priv->lock); > > > > +?????? status = readl(priv->base + ASPEED_PECI_INT_STS); > > > > +?????? writel(status, priv->base + ASPEED_PECI_INT_STS); > > > > +?????? priv->status |= (status & ASPEED_PECI_INT_MASK); > > > > + > > > > +?????? /* > > > > +??????? * In most cases, interrupt bits will be set one by one but also > > > > note > > > > +??????? * that multiple interrupt bits could be set at the same time. > > > > +??????? */ > > > > +?????? if (status & ASPEED_PECI_INT_BUS_TIMEOUT) > > > > +?????????????? dev_dbg_ratelimited(priv->dev, > > > > "ASPEED_PECI_INT_BUS_TIMEOUT\n"); > > > > + > > > > +?????? if (status & ASPEED_PECI_INT_BUS_CONTENTION) > > > > +?????????????? dev_dbg_ratelimited(priv->dev, > > > > "ASPEED_PECI_INT_BUS_CONTENTION\n"); > > > > + > > > > +?????? if (status & ASPEED_PECI_INT_WR_FCS_BAD) > > > > +?????????????? dev_dbg_ratelimited(priv->dev, > > > > "ASPEED_PECI_INT_WR_FCS_BAD\n"); > > > > + > > > > +?????? if (status & ASPEED_PECI_INT_WR_FCS_ABORT) > > > > +?????????????? dev_dbg_ratelimited(priv->dev, > > > > "ASPEED_PECI_INT_WR_FCS_ABORT\n"); > > > > > > Are you sure these would not be better as tracepoints? If you're > > > debugging an interrupt related failure, the ratelimiting might get in > > > your way when you really need to know when one of these error > > > interrupts fire relative to another event. > > > > Tracepoints are ABI(ish), and using a full blown tracepoint just for IRQ > > status > > would probably be too much. > > Tracepoints become ABI once someone ships tooling that depends on them > being there. These don't look? attractive for a tool, and they don't > look difficult to maintain if the interrupt handler needs to be > reworked. I.e. it would be trivial to keep a dead tracepoint around if > worse came to worse to keep a tool from failing to load. After more consideration, I would prefer to remove these logs for now - in case of error I'll log full status in xfer(). > > > I was thinking about something like trace_printk hidden under a > > "CONFIG_PECI_DEBUG" (see above), but perhaps that's something for the future > > improvement? > > Again trace_printk() is only for private builds. > > > > > > > > > > + > > > > +?????? /* > > > > +??????? * All commands should be ended up with a > > > > ASPEED_PECI_INT_CMD_DONE > > > > bit > > > > +??????? * set even in an error case. > > > > +??????? */ > > > > +?????? if (status & ASPEED_PECI_INT_CMD_DONE) > > > > +?????????????? complete(&priv->xfer_complete); > > > > > > Hmm, no need to check if there was a sequencing error, like a command > > > was never submitted? > > > > It's handled by checking if HW is idle in xfer before a command is sent, > > where > > we just expect a single interrupt per command. > > I'm asking how do you determine if this status was spurious, or there > was a sequencing error in the driver? I don't think we have any means to determine it. PECI itself doesn't provide any mechanism to verify it (there is no sequence number or tag to match request/response). We're relying on the fact that BMC is a requester and initiates communication with CPU - the interrupt won't be generated if BMC doesn't send any request. Thanks -Iwona From billy_tsai at aspeedtech.com Mon Aug 30 18:35:53 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Mon, 30 Aug 2021 08:35:53 +0000 Subject: [RESEND v4 12/15] iio: adc: aspeed: Add func to set sampling rate. In-Reply-To: <20210829163659.2f7f5974@jic23-huawei> References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250003.17P03KRU092474@twspam01.aspeedtech.com> <20210829163659.2f7f5974@jic23-huawei> Message-ID: Hi Jonathan, On 2021/8/29, 11:33 PM, "Jonathan Cameron" wrote: On Tue, 24 Aug 2021 17:12:40 +0800 Billy Tsai wrote: >> Add the function to set the sampling rate and keep the sampling period >> for a driver used to wait the lastest value. >> >> Signed-off-by: Billy Tsai > Why move the code as well as factoring out the setter function? > I doubt it does any harm, but I'd like to understand why you did it. > Jonathan >> + ret = clk_prepare_enable(data->clk_scaler->clk); >> + if (ret) >> + return ret; >> + >> + ret = devm_add_action_or_reset(data->dev, >> + aspeed_adc_clk_disable_unprepare, >> + data->clk_scaler->clk); >> + if (ret) >> + return ret; >> + >> + ret = aspeed_adc_set_sampling_rate(indio_dev, ASPEED_ADC_DEF_SAMPLING_RATE); >> + if (ret) >> + return ret; >> + >> ret = aspeed_adc_vref_config(indio_dev); >> if (ret) >> return ret; >> @@ -413,16 +445,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) >> } >> >> /* Start all channels in normal mode. */ > Why move this code up? Because the ADC clock is required when initializing the ADC device. In our system, the clock is always on. Thus, the legacy driver won't encounter any issues. I move the clk_prepare_enable ahead of initializing phase for making the driver probe logically closer to the hardware. >> - ret = clk_prepare_enable(data->clk_scaler->clk); >> - if (ret) >> - return ret; >> - >> - ret = devm_add_action_or_reset(data->dev, >> - aspeed_adc_clk_disable_unprepare, >> - data->clk_scaler->clk); >> - if (ret) >> - return ret; >> - >> adc_engine_control_reg_val = >> readl(data->base + ASPEED_REG_ENGINE_CONTROL); >> adc_engine_control_reg_val |= Best Regards, Billy Tsai From jic23 at kernel.org Mon Aug 30 19:52:13 2021 From: jic23 at kernel.org (Jonathan Cameron) Date: Mon, 30 Aug 2021 10:52:13 +0100 Subject: [RESEND v4 12/15] iio: adc: aspeed: Add func to set sampling rate. In-Reply-To: References: <20210824091243.9393-1-billy_tsai@aspeedtech.com> <202108250003.17P03KRU092474@twspam01.aspeedtech.com> <20210829163659.2f7f5974@jic23-huawei> Message-ID: <20210830105213.25ee20a2@jic23-huawei> On Mon, 30 Aug 2021 08:35:53 +0000 Billy Tsai wrote: > Hi Jonathan, > > On 2021/8/29, 11:33 PM, "Jonathan Cameron" wrote: > > On Tue, 24 Aug 2021 17:12:40 +0800 > Billy Tsai wrote: > > >> Add the function to set the sampling rate and keep the sampling period > >> for a driver used to wait the lastest value. > >> > >> Signed-off-by: Billy Tsai > > > Why move the code as well as factoring out the setter function? > > I doubt it does any harm, but I'd like to understand why you did it. > > > Jonathan > > >> + ret = clk_prepare_enable(data->clk_scaler->clk); > >> + if (ret) > >> + return ret; > >> + > >> + ret = devm_add_action_or_reset(data->dev, > >> + aspeed_adc_clk_disable_unprepare, > >> + data->clk_scaler->clk); > >> + if (ret) > >> + return ret; > >> + > >> + ret = aspeed_adc_set_sampling_rate(indio_dev, ASPEED_ADC_DEF_SAMPLING_RATE); > >> + if (ret) > >> + return ret; > >> + > >> ret = aspeed_adc_vref_config(indio_dev); > >> if (ret) > >> return ret; > >> @@ -413,16 +445,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) > >> } > >> > >> /* Start all channels in normal mode. */ > > > Why move this code up? > > Because the ADC clock is required when initializing the ADC device. > In our system, the clock is always on. Thus, the legacy driver won't encounter any issues. > I move the clk_prepare_enable ahead of initializing phase for making the driver probe logically closer to the hardware. Thanks. Please add something to the patch description to say this. Jonathan > > >> - ret = clk_prepare_enable(data->clk_scaler->clk); > >> - if (ret) > >> - return ret; > >> - > >> - ret = devm_add_action_or_reset(data->dev, > >> - aspeed_adc_clk_disable_unprepare, > >> - data->clk_scaler->clk); > >> - if (ret) > >> - return ret; > >> - > >> adc_engine_control_reg_val = > >> readl(data->base + ASPEED_REG_ENGINE_CONTROL); > >> adc_engine_control_reg_val |= > > > Best Regards, > Billy Tsai > From eajames at linux.ibm.com Tue Aug 31 07:46:43 2021 From: eajames at linux.ibm.com (Eddie James) Date: Mon, 30 Aug 2021 16:46:43 -0500 Subject: [PATCH 1/2] ARM: dts: aspeed: rainier: Add system LEDs Message-ID: <20210830214644.58539-1-eajames@linux.ibm.com> Add all the missing system LEDs. These are all connected by GPIO expanders. Signed-off-by: Eddie James --- arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts | 1541 +++++++++++++++--- 1 file changed, 1325 insertions(+), 216 deletions(-) diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts index d1467d5163f2..f222aea91b2e 100644 --- a/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts +++ b/arch/arm/boot/dts/aspeed-bmc-ibm-rainier.dts @@ -1126,6 +1126,62 @@ tmp275 at 4a { reg = <0x4a>; }; + pca9551 at 60 { + compatible = "nxp,pca9551"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "cablecard0-cxp-top"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "cablecard0-cxp-bot"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + gpio at 2 { + reg = <2>; + type = ; + }; + + gpio at 3 { + reg = <3>; + type = ; + }; + + gpio at 4 { + reg = <4>; + type = ; + }; + + gpio at 5 { + reg = <5>; + type = ; + }; + + gpio at 6 { + reg = <6>; + type = ; + }; + + gpio at 7 { + reg = <7>; + type = ; + }; + }; + pca9546 at 70 { compatible = "nxp,pca9546"; reg = <0x70>; @@ -1182,6 +1238,118 @@ tmp275 at 49 { reg = <0x49>; }; + pca9551 at 60 { + compatible = "nxp,pca9551"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "cablecard3-cxp-top"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "cablecard3-cxp-bot"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + gpio at 2 { + reg = <2>; + type = ; + }; + + gpio at 3 { + reg = <3>; + type = ; + }; + + gpio at 4 { + reg = <4>; + type = ; + }; + + gpio at 5 { + reg = <5>; + type = ; + }; + + gpio at 6 { + reg = <6>; + type = ; + }; + + gpio at 7 { + reg = <7>; + type = ; + }; + }; + + pca9551 at 61 { + compatible = "nxp,pca9551"; + reg = <0x61>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "cablecard4-cxp-top"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "cablecard4-cxp-bot"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + gpio at 2 { + reg = <2>; + type = ; + }; + + gpio at 3 { + reg = <3>; + type = ; + }; + + gpio at 4 { + reg = <4>; + type = ; + }; + + gpio at 5 { + reg = <5>; + type = ; + }; + + gpio at 6 { + reg = <6>; + type = ; + }; + + gpio at 7 { + reg = <7>; + type = ; + }; + }; + pca9546 at 70 { compatible = "nxp,pca9546"; reg = <0x70>; @@ -1290,101 +1458,69 @@ &i2c7 { multi-master; status = "okay"; - si7021-a20 at 40 { - compatible = "silabs,si7020"; - reg = <0x40>; - }; - - tmp275 at 48 { - compatible = "ti,tmp275"; - reg = <0x48>; - }; - - max: max31785 at 52 { - compatible = "maxim,max31785a"; - reg = <0x52>; - #address-cells = <1>; - #size-cells = <0>; - - fan0: fan at 0 { - compatible = "pmbus-fan"; - reg = <0>; - tach-pulses = <2>; - }; - - fan1: fan at 1 { - compatible = "pmbus-fan"; - reg = <1>; - tach-pulses = <2>; - }; - - fan2: fan at 2 { - compatible = "pmbus-fan"; - reg = <2>; - tach-pulses = <2>; - }; - - fan3: fan at 3 { - compatible = "pmbus-fan"; - reg = <3>; - tach-pulses = <2>; - }; - - fan4: fan at 4 { - compatible = "pmbus-fan"; - reg = <4>; - tach-pulses = <2>; - }; - - fan5: fan at 5 { - compatible = "pmbus-fan"; - reg = <5>; - tach-pulses = <2>; - }; - }; - - pca0: pca9552 at 61 { - compatible = "nxp,pca9552"; - reg = <0x61>; + pca9552 at 30 { + compatible = "ibm,pca9552"; + reg = <0x30>; #address-cells = <1>; #size-cells = <0>; gpio-controller; #gpio-cells = <2>; - gpio at 0 { + led at 0 { + label = "pcieslot0"; reg = <0>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 1 { + led at 1 { + label = "pcieslot1"; reg = <1>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 2 { + led at 2 { + label = "pcieslot2"; reg = <2>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 3 { + led at 3 { + label = "pcieslot3"; reg = <3>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 4 { + led at 4 { + label = "pcieslot4"; reg = <4>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 5 { + led at 5 { + label = "cpu1"; reg = <5>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 6 { + led at 6 { + label = "cpu-vrm1"; reg = <6>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; gpio at 7 { @@ -1392,9 +1528,12 @@ gpio at 7 { type = ; }; - gpio at 8 { + led at 8 { + label = "lcd-russel"; reg = <8>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; gpio at 9 { @@ -1433,116 +1572,955 @@ gpio at 15 { }; }; - ibm-panel at 62 { - compatible = "ibm,op-panel"; - reg = <(0x62 | I2C_OWN_SLAVE_ADDRESS)>; - }; + pca9552 at 31 { + compatible = "ibm,pca9552"; + reg = <0x31>; + #address-cells = <1>; + #size-cells = <0>; - dps: dps310 at 76 { - compatible = "infineon,dps310"; - reg = <0x76>; - #io-channel-cells = <0>; - }; + gpio-controller; + #gpio-cells = <2>; - eeprom at 50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; + led at 0 { + label = "ddimm0"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - eeprom at 51 { - compatible = "atmel,24c64"; - reg = <0x51>; - }; -}; + led at 1 { + label = "ddimm1"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; -&i2c8 { - status = "okay"; + led at 2 { + label = "ddimm2"; + reg = <2>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - ucd90320 at 11 { - compatible = "ti,ucd90320"; - reg = <0x11>; - }; + led at 3 { + label = "ddimm3"; + reg = <3>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - rtc at 32 { - compatible = "epson,rx8900"; - reg = <0x32>; - }; + led at 4 { + label = "ddimm4"; + reg = <4>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - tmp275 at 48 { - compatible = "ti,tmp275"; - reg = <0x48>; - }; + led at 5 { + label = "ddimm5"; + reg = <5>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - tmp275 at 4a { - compatible = "ti,tmp275"; - reg = <0x4a>; - }; + led at 6 { + label = "ddimm6"; + reg = <6>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 7 { + label = "ddimm7"; + reg = <7>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 8 { + label = "ddimm8"; + reg = <8>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 9 { + label = "ddimm9"; + reg = <9>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 10 { + label = "ddimm10"; + reg = <10>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 11 { + label = "ddimm11"; + reg = <11>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 12 { + label = "ddimm12"; + reg = <12>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 13 { + label = "ddimm13"; + reg = <13>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 14 { + label = "ddimm14"; + reg = <14>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 15 { + label = "ddimm15"; + reg = <15>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + }; + + pca9552 at 32 { + compatible = "ibm,pca9552"; + reg = <0x32>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "ddimm16"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "ddimm17"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 2 { + label = "ddimm18"; + reg = <2>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 3 { + label = "ddimm19"; + reg = <3>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 4 { + label = "ddimm20"; + reg = <4>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 5 { + label = "ddimm21"; + reg = <5>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 6 { + label = "ddimm22"; + reg = <6>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 7 { + label = "ddimm23"; + reg = <7>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 8 { + label = "ddimm24"; + reg = <8>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 9 { + label = "ddimm25"; + reg = <9>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 10 { + label = "ddimm26"; + reg = <10>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 11 { + label = "ddimm27"; + reg = <11>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 12 { + label = "ddimm28"; + reg = <12>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 13 { + label = "ddimm29"; + reg = <13>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 14 { + label = "ddimm30"; + reg = <14>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 15 { + label = "ddimm31"; + reg = <15>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + }; + + pca9552 at 33 { + compatible = "ibm,pca9552"; + reg = <0x33>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "planar"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "cpu0"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + gpio at 2 { + reg = <2>; + type = ; + }; + + led at 3 { + label = "dasd-pyramid0"; + reg = <3>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 4 { + label = "dasd-pyramid1"; + reg = <4>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 5 { + label = "dasd-pyramid2"; + reg = <5>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 6 { + label = "cpu0-vrm0"; + reg = <6>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 7 { + label = "rtc-battery"; + reg = <7>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 8 { + label = "base-blyth"; + reg = <8>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 9 { + label = "pcieslot6"; + reg = <9>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 10 { + label = "pcieslot7"; + reg = <10>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 11 { + label = "pcieslot8"; + reg = <11>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 12 { + label = "pcieslot9"; + reg = <12>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 13 { + label = "pcieslot10"; + reg = <13>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 14 { + label = "pcieslot11"; + reg = <14>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 15 { + label = "tpm-wilson"; + reg = <15>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + }; + + si7021-a20 at 40 { + compatible = "silabs,si7020"; + reg = <0x40>; + }; + + tmp275 at 48 { + compatible = "ti,tmp275"; + reg = <0x48>; + }; + + max: max31785 at 52 { + compatible = "maxim,max31785a"; + reg = <0x52>; + #address-cells = <1>; + #size-cells = <0>; + + fan0: fan at 0 { + compatible = "pmbus-fan"; + reg = <0>; + tach-pulses = <2>; + }; + + fan1: fan at 1 { + compatible = "pmbus-fan"; + reg = <1>; + tach-pulses = <2>; + }; + + fan2: fan at 2 { + compatible = "pmbus-fan"; + reg = <2>; + tach-pulses = <2>; + }; + + fan3: fan at 3 { + compatible = "pmbus-fan"; + reg = <3>; + tach-pulses = <2>; + }; + + fan4: fan at 4 { + compatible = "pmbus-fan"; + reg = <4>; + tach-pulses = <2>; + }; + + fan5: fan at 5 { + compatible = "pmbus-fan"; + reg = <5>; + tach-pulses = <2>; + }; + }; + + pca0: pca9552 at 61 { + compatible = "nxp,pca9552"; + reg = <0x61>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "fan0"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "fan1"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 2 { + label = "fan2"; + reg = <2>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 3 { + label = "fan3"; + reg = <3>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 4 { + label = "fan4"; + reg = <4>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 5 { + label = "fan5"; + reg = <5>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + gpio at 6 { + reg = <6>; + type = ; + }; + + gpio at 7 { + reg = <7>; + type = ; + }; + + gpio at 8 { + reg = <8>; + type = ; + }; + + gpio at 9 { + reg = <9>; + type = ; + }; + + gpio at 10 { + reg = <10>; + type = ; + }; + + gpio at 11 { + reg = <11>; + type = ; + }; + + gpio at 12 { + reg = <12>; + type = ; + }; + + gpio at 13 { + reg = <13>; + type = ; + }; + + gpio at 14 { + reg = <14>; + type = ; + }; + + gpio at 15 { + reg = <15>; + type = ; + }; + }; + + ibm-panel at 62 { + compatible = "ibm,op-panel"; + reg = <(0x62 | I2C_OWN_SLAVE_ADDRESS)>; + }; + + dps: dps310 at 76 { + compatible = "infineon,dps310"; + reg = <0x76>; + #io-channel-cells = <0>; + }; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + + eeprom at 51 { + compatible = "atmel,24c64"; + reg = <0x51>; + }; +}; + +&i2c8 { + status = "okay"; + + ucd90320 at 11 { + compatible = "ti,ucd90320"; + reg = <0x11>; + }; + + rtc at 32 { + compatible = "epson,rx8900"; + reg = <0x32>; + }; + + tmp275 at 48 { + compatible = "ti,tmp275"; + reg = <0x48>; + }; + + tmp275 at 4a { + compatible = "ti,tmp275"; + reg = <0x4a>; + }; eeprom at 50 { compatible = "atmel,24c64"; reg = <0x50>; }; - eeprom at 51 { + eeprom at 51 { + compatible = "atmel,24c64"; + reg = <0x51>; + }; + + pca_pres2: pca9552 at 61 { + compatible = "nxp,pca9552"; + reg = <0x61>; + #address-cells = <1>; + #size-cells = <0>; + gpio-controller; + #gpio-cells = <2>; + + gpio-line-names = + "SLOT6_PRSNT_EN_RSVD", "SLOT7_PRSNT_EN_RSVD", + "SLOT8_PRSNT_EN_RSVD", "SLOT9_PRSNT_EN_RSVD", + "SLOT10_PRSNT_EN_RSVD", "SLOT11_PRSNT_EN_RSVD", + "SLOT6_EXPANDER_PRSNT_N", "SLOT7_EXPANDER_PRSNT_N", + "SLOT8_EXPANDER_PRSNT_N", "SLOT9_EXPANDER_PRSNT_N", + "SLOT10_EXPANDER_PRSNT_N", "SLOT11_EXPANDER_PRSNT_N", + "", "", "", ""; + + gpio at 0 { + reg = <0>; + type = ; + }; + + gpio at 1 { + reg = <1>; + type = ; + }; + + gpio at 2 { + reg = <2>; + type = ; + }; + + gpio at 3 { + reg = <3>; + type = ; + }; + + gpio at 4 { + reg = <4>; + type = ; + }; + + gpio at 5 { + reg = <5>; + type = ; + }; + + gpio at 6 { + reg = <6>; + type = ; + }; + + gpio at 7 { + reg = <7>; + type = ; + }; + + gpio at 8 { + reg = <8>; + type = ; + }; + + gpio at 9 { + reg = <9>; + type = ; + }; + + gpio at 10 { + reg = <10>; + type = ; + }; + + gpio at 11 { + reg = <11>; + type = ; + }; + + gpio at 12 { + reg = <12>; + type = ; + }; + + gpio at 13 { + reg = <13>; + type = ; + }; + + gpio at 14 { + reg = <14>; + type = ; + }; + + gpio at 15 { + reg = <15>; + type = ; + }; + }; + +}; + +&i2c9 { + status = "okay"; + + tmp423a at 4c { + compatible = "ti,tmp423"; + reg = <0x4c>; + }; + + tmp423b at 4d { + compatible = "ti,tmp423"; + reg = <0x4d>; + }; + + eeprom at 50 { + compatible = "atmel,24c128"; + reg = <0x50>; + }; +}; + +&i2c10 { + status = "okay"; + + tmp423a at 4c { + compatible = "ti,tmp423"; + reg = <0x4c>; + }; + + tmp423b at 4d { + compatible = "ti,tmp423"; + reg = <0x4d>; + }; + + eeprom at 50 { + compatible = "atmel,24c128"; + reg = <0x50>; + }; +}; + +&i2c11 { + status = "okay"; + + tmp275 at 48 { + compatible = "ti,tmp275"; + reg = <0x48>; + }; + + tmp275 at 49 { + compatible = "ti,tmp275"; + reg = <0x49>; + }; + + pca9551 at 60 { + compatible = "nxp,pca9551"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "cablecard10-cxp-top"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "cablecard10-cxp-bot"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + gpio at 2 { + reg = <2>; + type = ; + }; + + gpio at 3 { + reg = <3>; + type = ; + }; + + gpio at 4 { + reg = <4>; + type = ; + }; + + gpio at 5 { + reg = <5>; + type = ; + }; + + gpio at 6 { + reg = <6>; + type = ; + }; + + gpio at 7 { + reg = <7>; + type = ; + }; + }; + + pca9546 at 70 { + compatible = "nxp,pca9546"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + status = "okay"; + i2c-mux-idle-disconnect; + + i2c11mux0chn0: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c11mux0chn1: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + eeprom at 51 { + compatible = "atmel,24c64"; + reg = <0x51>; + }; + }; + }; +}; + +&i2c12 { + status = "okay"; +}; + +&i2c13 { + status = "okay"; + + eeprom at 50 { compatible = "atmel,24c64"; - reg = <0x51>; + reg = <0x50>; }; - pca_pres2: pca9552 at 61 { + pca9552 at 60 { compatible = "nxp,pca9552"; - reg = <0x61>; + reg = <0x60>; #address-cells = <1>; #size-cells = <0>; + gpio-controller; #gpio-cells = <2>; - gpio-line-names = - "SLOT6_PRSNT_EN_RSVD", "SLOT7_PRSNT_EN_RSVD", - "SLOT8_PRSNT_EN_RSVD", "SLOT9_PRSNT_EN_RSVD", - "SLOT10_PRSNT_EN_RSVD", "SLOT11_PRSNT_EN_RSVD", - "SLOT6_EXPANDER_PRSNT_N", "SLOT7_EXPANDER_PRSNT_N", - "SLOT8_EXPANDER_PRSNT_N", "SLOT9_EXPANDER_PRSNT_N", - "SLOT10_EXPANDER_PRSNT_N", "SLOT11_EXPANDER_PRSNT_N", - "", "", "", ""; - - gpio at 0 { + led at 0 { + label = "nvme0"; reg = <0>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 1 { + led at 1 { + label = "nvme1"; reg = <1>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 2 { + led at 2 { + label = "nvme2"; reg = <2>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 3 { + led at 3 { + label = "nvme3"; reg = <3>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 4 { + led at 4 { + label = "nvme4"; reg = <4>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 5 { + led at 5 { + label = "nvme5"; reg = <5>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 6 { + led at 6 { + label = "nvme6"; reg = <6>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - gpio at 7 { + led at 7 { + label = "nvme7"; reg = <7>; - type = ; + retain-state-shutdown; + default-state = "keep"; + type = ; }; gpio at 8 { @@ -1585,111 +2563,128 @@ gpio at 15 { type = ; }; }; - }; -&i2c9 { +&i2c14 { status = "okay"; - tmp423a at 4c { - compatible = "ti,tmp423"; - reg = <0x4c>; - }; - - tmp423b at 4d { - compatible = "ti,tmp423"; - reg = <0x4d>; - }; - eeprom at 50 { - compatible = "atmel,24c128"; + compatible = "atmel,24c64"; reg = <0x50>; }; -}; -&i2c10 { - status = "okay"; + pca9552 at 60 { + compatible = "nxp,pca9552"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; - tmp423a at 4c { - compatible = "ti,tmp423"; - reg = <0x4c>; - }; + gpio-controller; + #gpio-cells = <2>; - tmp423b at 4d { - compatible = "ti,tmp423"; - reg = <0x4d>; - }; + led at 0 { + label = "nvme8"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - eeprom at 50 { - compatible = "atmel,24c128"; - reg = <0x50>; - }; -}; + led at 1 { + label = "nvme9"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; -&i2c11 { - status = "okay"; + led at 2 { + label = "nvme10"; + reg = <2>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - tmp275 at 48 { - compatible = "ti,tmp275"; - reg = <0x48>; - }; + led at 3 { + label = "nvme11"; + reg = <3>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - tmp275 at 49 { - compatible = "ti,tmp275"; - reg = <0x49>; - }; + led at 4 { + label = "nvme12"; + reg = <4>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - pca9546 at 70 { - compatible = "nxp,pca9546"; - reg = <0x70>; - #address-cells = <1>; - #size-cells = <0>; - status = "okay"; - i2c-mux-idle-disconnect; + led at 5 { + label = "nvme13"; + reg = <5>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - i2c11mux0chn0: i2c at 0 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0>; + led at 6 { + label = "nvme14"; + reg = <6>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; - eeprom at 50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; + led at 7 { + label = "nvme15"; + reg = <7>; + retain-state-shutdown; + default-state = "keep"; + type = ; }; - i2c11mux0chn1: i2c at 1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <1>; + gpio at 8 { + reg = <8>; + type = ; + }; - eeprom at 51 { - compatible = "atmel,24c64"; - reg = <0x51>; - }; + gpio at 9 { + reg = <9>; + type = ; }; - }; -}; -&i2c12 { - status = "okay"; -}; + gpio at 10 { + reg = <10>; + type = ; + }; -&i2c13 { - status = "okay"; + gpio at 11 { + reg = <11>; + type = ; + }; - eeprom at 50 { - compatible = "atmel,24c64"; - reg = <0x50>; - }; -}; + gpio at 12 { + reg = <12>; + type = ; + }; -&i2c14 { - status = "okay"; + gpio at 13 { + reg = <13>; + type = ; + }; - eeprom at 50 { - compatible = "atmel,24c64"; - reg = <0x50>; + gpio at 14 { + reg = <14>; + type = ; + }; + + gpio at 15 { + reg = <15>; + type = ; + }; }; }; @@ -1700,6 +2695,120 @@ eeprom at 50 { compatible = "atmel,24c64"; reg = <0x50>; }; + + pca9552 at 60 { + compatible = "nxp,pca9552"; + reg = <0x60>; + #address-cells = <1>; + #size-cells = <0>; + + gpio-controller; + #gpio-cells = <2>; + + led at 0 { + label = "nvme16"; + reg = <0>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 1 { + label = "nvme17"; + reg = <1>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 2 { + label = "nvme18"; + reg = <2>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 3 { + label = "nvme19"; + reg = <3>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 4 { + label = "nvme20"; + reg = <4>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 5 { + label = "nvme21"; + reg = <5>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 6 { + label = "nvme22"; + reg = <6>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + led at 7 { + label = "nvme23"; + reg = <7>; + retain-state-shutdown; + default-state = "keep"; + type = ; + }; + + gpio at 8 { + reg = <8>; + type = ; + }; + + gpio at 9 { + reg = <9>; + type = ; + }; + + gpio at 10 { + reg = <10>; + type = ; + }; + + gpio at 11 { + reg = <11>; + type = ; + }; + + gpio at 12 { + reg = <12>; + type = ; + }; + + gpio at 13 { + reg = <13>; + type = ; + }; + + gpio at 14 { + reg = <14>; + type = ; + }; + + gpio at 15 { + reg = <15>; + type = ; + }; + }; }; &vuart1 { -- 2.27.0 From eajames at linux.ibm.com Tue Aug 31 07:46:44 2021 From: eajames at linux.ibm.com (Eddie James) Date: Mon, 30 Aug 2021 16:46:44 -0500 Subject: [PATCH 2/2] ARM: dts: aspeed: everest: Add I2C bus 15 muxes In-Reply-To: <20210830214644.58539-1-eajames@linux.ibm.com> References: <20210830214644.58539-1-eajames@linux.ibm.com> Message-ID: <20210830214644.58539-2-eajames@linux.ibm.com> Add the muxes that are attached on I2C bus 15. Signed-off-by: Eddie James --- arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts | 158 +++++++++++++++++++ 1 file changed, 158 insertions(+) diff --git a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts index 2efd70666738..e7da58595d14 100644 --- a/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts +++ b/arch/arm/boot/dts/aspeed-bmc-ibm-everest.dts @@ -96,6 +96,18 @@ aliases { i2c32 = &i2c14mux1chn1; i2c33 = &i2c14mux1chn2; i2c34 = &i2c14mux1chn3; + i2c35 = &i2c15mux0chn0; + i2c36 = &i2c15mux0chn1; + i2c37 = &i2c15mux0chn2; + i2c38 = &i2c15mux0chn3; + i2c39 = &i2c15mux1chn0; + i2c40 = &i2c15mux1chn1; + i2c41 = &i2c15mux1chn2; + i2c42 = &i2c15mux1chn3; + i2c43 = &i2c15mux2chn0; + i2c44 = &i2c15mux2chn1; + i2c45 = &i2c15mux2chn2; + i2c46 = &i2c15mux2chn3; serial4 = &uart5; @@ -2816,6 +2828,152 @@ eeprom at 50 { &i2c15 { status = "okay"; + + i2c-switch at 70 { + compatible = "nxp,pca9546"; + reg = <0x70>; + #address-cells = <1>; + #size-cells = <0>; + i2c-mux-idle-disconnect; + + i2c15mux0chn0: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux0chn1: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux0chn2: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux0chn3: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + }; + + i2c-switch at 71 { + compatible = "nxp,pca9546"; + reg = <0x71>; + #address-cells = <1>; + #size-cells = <0>; + i2c-mux-idle-disconnect; + + i2c15mux1chn0: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux1chn1: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux1chn2: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux1chn3: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + }; + + i2c-switch at 72 { + compatible = "nxp,pca9546"; + reg = <0x72>; + #address-cells = <1>; + #size-cells = <0>; + i2c-mux-idle-disconnect; + + i2c15mux2chn0: i2c at 0 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux2chn1: i2c at 1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <1>; + + eeprom at 50 { + compatible = "atmel,24c64"; + reg = <0x50>; + }; + }; + + i2c15mux2chn2: i2c at 2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <2>; + }; + + i2c15mux2chn3: i2c at 3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <3>; + }; + }; }; &ehci1 { -- 2.27.0 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:44 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:44 +0800 Subject: [v5 01/15] iio: adc: aspeed: set driver data when adc probe. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-2-billy_tsai@aspeedtech.com> Fix the issue when adc remove will get the null driver data. Fixed: commit 573803234e72 ("iio: Aspeed ADC") Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 19efaa41bc34..34ec0c28b2df 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -183,6 +183,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(data->base)) -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:43 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:43 +0800 Subject: [v5 00/15] Add support for ast2600 ADC Message-ID: <20210831071458.2334-1-billy_tsai@aspeedtech.com> This patch serials make aspeed_adc.c can support ast2600 and backward compatible. Change since v4: dt-bindings: - Add clocks maxItems. - Rename the property to meet the property-units.yaml. - Add the description for the difference between adc0 and adc1. aspeed_adc.c: - Use new property name to get internal reference voltage: units from mv to uv. - Fix -Wnonnull warning caused by snprintf parameters. - Add suffix mv to the vref parameters. - Use ARRAY_SIZE instead of 32. - Add a reset action for ADC power down and Use devm_iio_device_register. - Fix typo error. - Separate the offset interface of ch7 when battery sensing enable Change since v3: dt-bindings: - Fix properties:aspeed,int_vref_mv type error. Change since v2: dt-bindings: - Create a new dt-bindings for ast2600 adc aspeed_adc.c: - Splits the patch for more details - Remove version enum and use the flags in model data to distinguish hardware feature - Support trimming data get and set. - Use devm_add_action_or_reset to simplify probe error handling. Changes since v1: dt-bindings: - Fix the aspeed,adc.yaml check error. - Add battery-sensing property. aspeed_adc.c: - Change the init flow: Clock and reference voltage setting should be completed before adc engine enable. - Change the default sampling rate to meet most user case. - Add patch #8 to suppoert battery sensing mode. Billy Tsai (15): iio: adc: aspeed: set driver data when adc probe. dt-bindings: iio: adc: Add ast2600-adc bindings iio: adc: aspeed: completes the bitfield declare. iio: adc: aspeed: Keep model data to driver data. iio: adc: aspeed: Refactory model data structure iio: adc: aspeed: Add vref config function iio: adc: aspeed: Set num_channels with model data iio: adc: aspeed: Use model_data to set clk scaler. iio: adc: aspeed: Use devm_add_action_or_reset. iio: adc: aspeed: Support ast2600 adc. iio: adc: aspeed: Fix the calculate error of clock. iio: adc: aspeed: Add func to set sampling rate. iio: adc: aspeed: Add compensation phase. iio: adc: aspeed: Support battery sensing. iio: adc: aspeed: Get and set trimming data. .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 100 +++ drivers/iio/adc/aspeed_adc.c | 617 +++++++++++++++--- 2 files changed, 619 insertions(+), 98 deletions(-) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:49 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:49 +0800 Subject: [v5 06/15] iio: adc: aspeed: Add vref config function In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-7-billy_tsai@aspeedtech.com> Add the function to check the vref_fixed_mv and set the value to driver data. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 6ce2f676c54a..01df24f6d3f1 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -130,7 +130,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_fixed_mv; + *val = data->vref_mv; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -195,6 +195,17 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static int aspeed_adc_vref_config(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (data->model_data->vref_fixed_mv) { + data->vref_mv = data->model_data->vref_fixed_mv; + return 0; + } + return 0; +} + static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -250,6 +261,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); + ret = aspeed_adc_vref_config(indio_dev); + if (ret) + goto vref_config_error; + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, @@ -298,6 +313,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: +vref_config_error: reset_control_assert(data->rst); reset_error: clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:47 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:47 +0800 Subject: [v5 04/15] iio: adc: aspeed: Keep model data to driver data. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-5-billy_tsai@aspeedtech.com> Keep the model data pointer to driver data for reducing the usage of of_device_get_match_data(). Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index f055fe7b2c40..76ae1c3f584b 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -77,6 +77,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; + const struct aspeed_adc_model_data *model_data; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -118,8 +119,6 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -127,7 +126,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = model_data->vref_voltage; + *val = data->model_data->vref_voltage; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -146,13 +145,11 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); - const struct aspeed_adc_model_data *model_data = - of_device_get_match_data(data->dev); switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < model_data->min_sampling_rate || - val > model_data->max_sampling_rate) + if (val < data->model_data->min_sampling_rate || + val > data->model_data->max_sampling_rate) return -EINVAL; clk_set_rate(data->clk_scaler->clk, @@ -198,7 +195,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const struct aspeed_adc_model_data *model_data; const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; @@ -209,6 +205,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) data = iio_priv(indio_dev); data->dev = &pdev->dev; + data->model_data = of_device_get_match_data(&pdev->dev); platform_set_drvdata(pdev, indio_dev); data->base = devm_platform_ioremap_resource(pdev, 0); @@ -249,9 +246,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) } reset_control_deassert(data->rst); - model_data = of_device_get_match_data(&pdev->dev); - - if (model_data->wait_init_sequence) { + if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | @@ -281,8 +276,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); - model_data = of_device_get_match_data(&pdev->dev); - indio_dev->name = model_data->model_name; + indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:45 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:45 +0800 Subject: [v5 02/15] dt-bindings: iio: adc: Add ast2600-adc bindings In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-3-billy_tsai@aspeedtech.com> Add device tree bindings document for the aspeed ast2600 adc device driver. Signed-off-by: Billy Tsai --- .../bindings/iio/adc/aspeed,ast2600-adc.yaml | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml diff --git a/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml new file mode 100644 index 000000000000..b283c8ca2bbf --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/aspeed,ast2600-adc.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/iio/adc/aspeed,ast2600-adc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ADC that forms part of an ASPEED server management processor. + +maintainers: + - Billy Tsai + +description: | + ? 10-bits resolution for 16 voltage channels. + ? The device split into two individual engine and each contains 8 voltage + channels. + ? Channel scanning can be non-continuous. + ? Programmable ADC clock frequency. + ? Programmable upper and lower threshold for each channels. + ? Interrupt when larger or less than threshold for each channels. + ? Support hysteresis for each channels. + ? Built-in a compensating method. + ? Built-in a register to trim internal reference voltage. + ? Internal or External reference voltage. + ? Support 2 Internal reference voltage 1.2v or 2.5v. + ? Integrate dividing circuit for battery sensing. + +properties: + compatible: + enum: + - aspeed,ast2600-adc0 + - aspeed,ast2600-adc1 + description: + Their trimming data, which is used to calibrate internal reference volage, + locates in different address of OTP. + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + description: + Input clock used to derive the sample clock. Expected to be the + SoC's APB clock. + + resets: + maxItems: 1 + + "#io-channel-cells": + const: 1 + + vref-supply: + description: + The external regulator supply ADC reference voltage. + + aspeed,int-vref-microvolt: + enum: [1200000, 2500000] + description: + ADC internal reference voltage in microvolts. + + aspeed,battery-sensing: + type: boolean + description: + Inform the driver that last channel will be used to sensor battery. + + aspeed,trim-data-valid: + type: boolean + description: | + The ADC reference voltage can be calibrated to obtain the trimming + data which will be stored in otp. This property informs the driver that + the data store in the otp is valid. + +required: + - compatible + - reg + - clocks + - resets + - "#io-channel-cells" + +additionalProperties: false + +examples: + - | + #include + adc0: adc at 1e6e9000 { + compatible = "aspeed,ast2600-adc0"; + reg = <0x1e6e9000 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int-vref-microvolt = <2500000>; + }; + adc1: adc at 1e6e9100 { + compatible = "aspeed,ast2600-adc1"; + reg = <0x1e6e9100 0x100>; + clocks = <&syscon ASPEED_CLK_APB2>; + resets = <&syscon ASPEED_RESET_ADC>; + #io-channel-cells = <1>; + aspeed,int-vref-microvolt = <2500000>; + }; +... -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:46 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:46 +0800 Subject: [v5 03/15] iio: adc: aspeed: completes the bitfield declare. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-4-billy_tsai@aspeedtech.com> This patch completes the declare of ADC register bitfields and uses the same prefix ASPEED_ADC_* for these bitfields. In addition, tidy up space alignment of the codes. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 64 ++++++++++++++++++++++++++---------- 1 file changed, 47 insertions(+), 17 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 34ec0c28b2df..f055fe7b2c40 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -3,6 +3,7 @@ * Aspeed AST2400/2500 ADC * * Copyright (C) 2017 Google, Inc. + * Copyright (C) 2021 Aspeed Technology Inc. */ #include @@ -16,6 +17,7 @@ #include #include #include +#include #include #include @@ -28,15 +30,39 @@ #define ASPEED_REG_INTERRUPT_CONTROL 0x04 #define ASPEED_REG_VGA_DETECT_CONTROL 0x08 #define ASPEED_REG_CLOCK_CONTROL 0x0C -#define ASPEED_REG_MAX 0xC0 - -#define ASPEED_OPERATION_MODE_POWER_DOWN (0x0 << 1) -#define ASPEED_OPERATION_MODE_STANDBY (0x1 << 1) -#define ASPEED_OPERATION_MODE_NORMAL (0x7 << 1) - -#define ASPEED_ENGINE_ENABLE BIT(0) - -#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_REG_COMPENSATION_TRIM 0xC4 +/* + * The register offset between 0xC8~0xCC can be read and won't affect the + * hardware logic in each version of ADC. + */ +#define ASPEED_REG_MAX 0xD0 + +#define ASPEED_ADC_ENGINE_ENABLE BIT(0) +#define ASPEED_ADC_OP_MODE GENMASK(3, 1) +#define ASPEED_ADC_OP_MODE_PWR_DOWN 0 +#define ASPEED_ADC_OP_MODE_STANDBY 1 +#define ASPEED_ADC_OP_MODE_NORMAL 7 +#define ASPEED_ADC_CTRL_COMPENSATION BIT(4) +#define ASPEED_ADC_AUTO_COMPENSATION BIT(5) +/* + * Bit 6 determines not only the reference voltage range but also the dividing + * circuit for battery sensing. + */ +#define ASPEED_ADC_REF_VOLTAGE GENMASK(7, 6) +#define ASPEED_ADC_REF_VOLTAGE_2500mV 0 +#define ASPEED_ADC_REF_VOLTAGE_1200mV 1 +#define ASPEED_ADC_REF_VOLTAGE_EXT_HIGH 2 +#define ASPEED_ADC_REF_VOLTAGE_EXT_LOW 3 +#define ASPEED_ADC_BAT_SENSING_DIV BIT(6) +#define ASPEED_ADC_BAT_SENSING_DIV_2_3 0 +#define ASPEED_ADC_BAT_SENSING_DIV_1_3 1 +#define ASPEED_ADC_CTRL_INIT_RDY BIT(8) +#define ASPEED_ADC_CH7_MODE BIT(12) +#define ASPEED_ADC_CH7_NORMAL 0 +#define ASPEED_ADC_CH7_BAT 1 +#define ASPEED_ADC_BAT_SENSING_ENABLE BIT(13) +#define ASPEED_ADC_CTRL_CHANNEL GENMASK(31, 16) +#define ASPEED_ADC_CTRL_CHANNEL_ENABLE(ch) FIELD_PREP(ASPEED_ADC_CTRL_CHANNEL, BIT(ch)) #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 @@ -227,7 +253,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (model_data->wait_init_sequence) { /* Enable engine in normal mode. */ - writel(ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE, + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); /* Wait for initial sequence complete. */ @@ -246,10 +274,12 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) goto clk_enable_error; - adc_engine_control_reg_val = GENMASK(31, 16) | - ASPEED_OPERATION_MODE_NORMAL | ASPEED_ENGINE_ENABLE; + adc_engine_control_reg_val = + ASPEED_ADC_CTRL_CHANNEL | + FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; writel(adc_engine_control_reg_val, - data->base + ASPEED_REG_ENGINE_CONTROL); + data->base + ASPEED_REG_ENGINE_CONTROL); model_data = of_device_get_match_data(&pdev->dev); indio_dev->name = model_data->model_name; @@ -265,8 +295,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) return 0; iio_register_error: - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); clk_enable_error: poll_timeout_error: @@ -284,8 +314,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) struct aspeed_adc_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); - writel(ASPEED_OPERATION_MODE_POWER_DOWN, - data->base + ASPEED_REG_ENGINE_CONTROL); + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + data->base + ASPEED_REG_ENGINE_CONTROL); clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:48 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:48 +0800 Subject: [v5 05/15] iio: adc: aspeed: Refactory model data structure In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-6-billy_tsai@aspeedtech.com> This patch refactors the model data structure to distinguish the function form different versions of aspeed ADC. - Rename the vref_voltage to vref_fixed_mv and add vref_mv driver data When driver probe will check vref_fixed_mv value and store it to vref_mv which isn't const value. - Add num_channels Make num_channles of iio device can be changed by different model_data - Add need_prescaler flag and scaler_bit_width The need_prescaler flag is used to tell the driver the clock divider needs another Prescaler and the scaler_bit_width to set the clock divider bitfield width. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 76ae1c3f584b..6ce2f676c54a 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -71,8 +71,11 @@ struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz unsigned int max_sampling_rate; // Hz - unsigned int vref_voltage; // mV + unsigned int vref_fixed_mv; bool wait_init_sequence; + bool need_prescaler; + u8 scaler_bit_width; + unsigned int num_channels; }; struct aspeed_adc_data { @@ -83,6 +86,7 @@ struct aspeed_adc_data { struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; + int vref_mv; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -126,7 +130,7 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = data->model_data->vref_voltage; + *val = data->model_data->vref_fixed_mv; *val2 = ASPEED_RESOLUTION_BITS; return IIO_VAL_FRACTIONAL_LOG2; @@ -320,17 +324,23 @@ static int aspeed_adc_remove(struct platform_device *pdev) static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", - .vref_voltage = 2500, // mV + .vref_fixed_mv = 2500, .min_sampling_rate = 10000, .max_sampling_rate = 500000, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct aspeed_adc_model_data ast2500_model_data = { .model_name = "ast2500-adc", - .vref_voltage = 1800, // mV + .vref_fixed_mv = 1800, .min_sampling_rate = 1, .max_sampling_rate = 1000000, .wait_init_sequence = true, + .need_prescaler = true, + .scaler_bit_width = 10, + .num_channels = 16, }; static const struct of_device_id aspeed_adc_matches[] = { -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:50 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:50 +0800 Subject: [v5 07/15] iio: adc: aspeed: Set num_channels with model data In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-8-billy_tsai@aspeedtech.com> Use the model_data member num_channels to set the num_channels of iio device. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 01df24f6d3f1..aec335956310 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -299,7 +299,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = aspeed_adc_iio_channels; - indio_dev->num_channels = ARRAY_SIZE(aspeed_adc_iio_channels); + indio_dev->num_channels = data->model_data->num_channels; ret = iio_device_register(indio_dev); if (ret) -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:52 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:52 +0800 Subject: [v5 09/15] iio: adc: aspeed: Use devm_add_action_or_reset. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-10-billy_tsai@aspeedtech.com> This patch use devm_add_action_or_reset to handle the error in probe phase. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 112 +++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 46 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index e53d1541ad1d..af00d9752c1e 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -195,6 +195,35 @@ static const struct iio_info aspeed_adc_iio_info = { .debugfs_reg_access = aspeed_adc_reg_access, }; +static void aspeed_adc_unregister_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_divider(clk); +} + +static void aspeed_adc_reset_assert(void *data) +{ + struct reset_control *rst = data; + + reset_control_assert(rst); +} + +static void aspeed_adc_clk_disable_unprepare(void *data) +{ + struct clk *clk = data; + + clk_disable_unprepare(clk); +} + +static void aspeed_adc_power_down(void *data) +{ + struct aspeed_adc_data *priv_data = data; + + writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), + priv_data->base + ASPEED_REG_ENGINE_CONTROL); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -242,6 +271,11 @@ static int aspeed_adc_probe(struct platform_device *pdev) &data->clk_lock); if (IS_ERR(data->clk_prescaler)) return PTR_ERR(data->clk_prescaler); + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_divider, + data->clk_prescaler); + if (ret) + return ret; snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name); scaler_flags = CLK_SET_RATE_PARENT; @@ -256,23 +290,30 @@ static int aspeed_adc_probe(struct platform_device *pdev) &pdev->dev, clk_name, clk_parent_name, scaler_flags, data->base + ASPEED_REG_CLOCK_CONTROL, 0, data->model_data->scaler_bit_width, 0, &data->clk_lock); - if (IS_ERR(data->clk_scaler)) { - ret = PTR_ERR(data->clk_scaler); - goto scaler_error; - } + if (IS_ERR(data->clk_scaler)) + return PTR_ERR(data->clk_scaler); + + ret = devm_add_action_or_reset(data->dev, aspeed_adc_unregister_divider, + data->clk_scaler); + if (ret) + return ret; data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); - ret = PTR_ERR(data->rst); - goto reset_error; + return PTR_ERR(data->rst); } reset_control_deassert(data->rst); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_reset_assert, + data->rst); + if (ret) + return ret; + ret = aspeed_adc_vref_config(indio_dev); if (ret) - goto vref_config_error; + return ret; if (data->model_data->wait_init_sequence) { /* Enable engine in normal mode. */ @@ -281,6 +322,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_ENGINE_ENABLE, data->base + ASPEED_REG_ENGINE_CONTROL); + ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down, + data); + if (ret) + return ret; /* Wait for initial sequence complete. */ ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, adc_engine_control_reg_val, @@ -289,13 +334,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_INIT_POLLING_TIME, ASPEED_ADC_INIT_TIMEOUT); if (ret) - goto poll_timeout_error; + return ret; } /* Start all channels in normal mode. */ ret = clk_prepare_enable(data->clk_scaler->clk); if (ret) - goto clk_enable_error; + return ret; + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; adc_engine_control_reg_val = ASPEED_ADC_CTRL_CHANNEL | @@ -303,6 +354,11 @@ static int aspeed_adc_probe(struct platform_device *pdev) ASPEED_ADC_ENGINE_ENABLE; writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_power_down, + data); + if (ret) + return ret; indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; @@ -310,45 +366,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->channels = aspeed_adc_iio_channels; indio_dev->num_channels = data->model_data->num_channels; - ret = iio_device_register(indio_dev); - if (ret) - goto iio_register_error; - - return 0; - -iio_register_error: - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); -clk_enable_error: -poll_timeout_error: -vref_config_error: - reset_control_assert(data->rst); -reset_error: - clk_hw_unregister_divider(data->clk_scaler); -scaler_error: - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); + ret = devm_iio_device_register(data->dev, indio_dev); return ret; } -static int aspeed_adc_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct aspeed_adc_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_PWR_DOWN), - data->base + ASPEED_REG_ENGINE_CONTROL); - clk_disable_unprepare(data->clk_scaler->clk); - reset_control_assert(data->rst); - clk_hw_unregister_divider(data->clk_scaler); - if (data->model_data->need_prescaler) - clk_hw_unregister_divider(data->clk_prescaler); - - return 0; -} - static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", .vref_fixed_mv = 2500, @@ -379,7 +400,6 @@ MODULE_DEVICE_TABLE(of, aspeed_adc_matches); static struct platform_driver aspeed_adc_driver = { .probe = aspeed_adc_probe, - .remove = aspeed_adc_remove, .driver = { .name = KBUILD_MODNAME, .of_match_table = aspeed_adc_matches, -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:51 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:51 +0800 Subject: [v5 08/15] iio: adc: aspeed: Use model_data to set clk scaler. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-9-billy_tsai@aspeedtech.com> This patch uses need_prescaler and scaler_bit_width to set the ADC clock scaler. Reported-by: kernel test robot Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 43 ++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 16 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index aec335956310..e53d1541ad1d 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -210,9 +210,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; struct aspeed_adc_data *data; - const char *clk_parent_name; int ret; u32 adc_engine_control_reg_val; + unsigned long scaler_flags = 0; + char clk_name[32], clk_parent_name[32]; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*data)); if (!indio_dev) @@ -229,24 +230,32 @@ static int aspeed_adc_probe(struct platform_device *pdev) /* Register ADC clock prescaler with source specified by device tree. */ spin_lock_init(&data->clk_lock); - clk_parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); - - data->clk_prescaler = clk_hw_register_divider( - &pdev->dev, "prescaler", clk_parent_name, 0, - data->base + ASPEED_REG_CLOCK_CONTROL, - 17, 15, 0, &data->clk_lock); - if (IS_ERR(data->clk_prescaler)) - return PTR_ERR(data->clk_prescaler); - + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s", + of_clk_get_parent_name(pdev->dev.of_node, 0)); + + if (data->model_data->need_prescaler) { + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", + data->model_data->model_name); + data->clk_prescaler = clk_hw_register_divider( + &pdev->dev, clk_name, clk_parent_name, 0, + data->base + ASPEED_REG_CLOCK_CONTROL, 17, 15, 0, + &data->clk_lock); + if (IS_ERR(data->clk_prescaler)) + return PTR_ERR(data->clk_prescaler); + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), + clk_name); + scaler_flags = CLK_SET_RATE_PARENT; + } /* * Register ADC clock scaler downstream from the prescaler. Allow rate * setting to adjust the prescaler as well. */ + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-scaler", + data->model_data->model_name); data->clk_scaler = clk_hw_register_divider( - &pdev->dev, "scaler", "prescaler", - CLK_SET_RATE_PARENT, - data->base + ASPEED_REG_CLOCK_CONTROL, - 0, 10, 0, &data->clk_lock); + &pdev->dev, clk_name, clk_parent_name, scaler_flags, + data->base + ASPEED_REG_CLOCK_CONTROL, 0, + data->model_data->scaler_bit_width, 0, &data->clk_lock); if (IS_ERR(data->clk_scaler)) { ret = PTR_ERR(data->clk_scaler); goto scaler_error; @@ -318,7 +327,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) reset_error: clk_hw_unregister_divider(data->clk_scaler); scaler_error: - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return ret; } @@ -333,7 +343,8 @@ static int aspeed_adc_remove(struct platform_device *pdev) clk_disable_unprepare(data->clk_scaler->clk); reset_control_assert(data->rst); clk_hw_unregister_divider(data->clk_scaler); - clk_hw_unregister_divider(data->clk_prescaler); + if (data->model_data->need_prescaler) + clk_hw_unregister_divider(data->clk_prescaler); return 0; } -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:53 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:53 +0800 Subject: [v5 10/15] iio: adc: aspeed: Support ast2600 adc. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-11-billy_tsai@aspeedtech.com> Make driver to support ast2600 adc device. - Use shared reset controller - Complete the vref configure function - Add the model data for ast2600 adc Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 105 +++++++++++++++++++++++++++++++++-- 1 file changed, 99 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index af00d9752c1e..40b7ba58c1dc 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Aspeed AST2400/2500 ADC + * Aspeed AST2400/2500/2600 ADC * * Copyright (C) 2017 Google, Inc. * Copyright (C) 2021 Aspeed Technology Inc. @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -81,6 +82,7 @@ struct aspeed_adc_model_data { struct aspeed_adc_data { struct device *dev; const struct aspeed_adc_model_data *model_data; + struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; struct clk_hw *clk_prescaler; @@ -224,14 +226,79 @@ static void aspeed_adc_power_down(void *data) priv_data->base + ASPEED_REG_ENGINE_CONTROL); } +static void aspeed_adc_reg_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + static int aspeed_adc_vref_config(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); + int ret; + u32 adc_engine_control_reg_val; if (data->model_data->vref_fixed_mv) { data->vref_mv = data->model_data->vref_fixed_mv; return 0; } + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + data->regulator = devm_regulator_get_optional(data->dev, "vref"); + if (!IS_ERR(data->regulator)) { + ret = regulator_enable(data->regulator); + if (ret) + return ret; + ret = devm_add_action_or_reset( + data->dev, aspeed_adc_reg_disable, data->regulator); + if (ret) + return ret; + data->vref_mv = regulator_get_voltage(data->regulator); + /* Conversion from uV to mV */ + data->vref_mv /= 1000; + if ((data->vref_mv >= 1550) && (data->vref_mv <= 2700)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_HIGH), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if ((data->vref_mv >= 900) && (data->vref_mv <= 1650)) + writel(adc_engine_control_reg_val | + FIELD_PREP( + ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_EXT_LOW), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Regulator voltage %d not support", + data->vref_mv); + return -EOPNOTSUPP; + } + } else { + if (PTR_ERR(data->regulator) != -ENODEV) + return PTR_ERR(data->regulator); + data->vref_mv = 2500000; + of_property_read_u32(data->dev->of_node, + "aspeed,int-vref-microvolt", + &data->vref_mv); + /* Conversion from uV to mV */ + data->vref_mv /= 1000; + if (data->vref_mv == 2500) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_2500mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else if (data->vref_mv == 1200) + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_REF_VOLTAGE, + ASPEED_ADC_REF_VOLTAGE_1200mV), + data->base + ASPEED_REG_ENGINE_CONTROL); + else { + dev_err(data->dev, "Voltage %d not support", data->vref_mv); + return -EOPNOTSUPP; + } + } + return 0; } @@ -298,7 +365,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; - data->rst = devm_reset_control_get_exclusive(&pdev->dev, NULL); + data->rst = devm_reset_control_get_shared(&pdev->dev, NULL); if (IS_ERR(data->rst)) { dev_err(&pdev->dev, "invalid or missing reset controller device tree entry"); @@ -316,10 +383,14 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; if (data->model_data->wait_init_sequence) { + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= + FIELD_PREP(ASPEED_ADC_OP_MODE, + ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE; /* Enable engine in normal mode. */ - writel(FIELD_PREP(ASPEED_ADC_OP_MODE, - ASPEED_ADC_OP_MODE_NORMAL) | - ASPEED_ADC_ENGINE_ENABLE, + writel(adc_engine_control_reg_val, data->base + ASPEED_REG_ENGINE_CONTROL); ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down, @@ -349,6 +420,8 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val |= ASPEED_ADC_CTRL_CHANNEL | FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | ASPEED_ADC_ENGINE_ENABLE; @@ -391,9 +464,29 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .num_channels = 16, }; +static const struct aspeed_adc_model_data ast2600_adc0_model_data = { + .model_name = "ast2600-adc0", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .scaler_bit_width = 16, + .num_channels = 8, +}; + +static const struct aspeed_adc_model_data ast2600_adc1_model_data = { + .model_name = "ast2600-adc1", + .min_sampling_rate = 10000, + .max_sampling_rate = 500000, + .wait_init_sequence = true, + .scaler_bit_width = 16, + .num_channels = 8, +}; + static const struct of_device_id aspeed_adc_matches[] = { { .compatible = "aspeed,ast2400-adc", .data = &ast2400_model_data }, { .compatible = "aspeed,ast2500-adc", .data = &ast2500_model_data }, + { .compatible = "aspeed,ast2600-adc0", .data = &ast2600_adc0_model_data }, + { .compatible = "aspeed,ast2600-adc1", .data = &ast2600_adc1_model_data }, {}, }; MODULE_DEVICE_TABLE(of, aspeed_adc_matches); @@ -409,5 +502,5 @@ static struct platform_driver aspeed_adc_driver = { module_platform_driver(aspeed_adc_driver); MODULE_AUTHOR("Rick Altherr "); -MODULE_DESCRIPTION("Aspeed AST2400/2500 ADC Driver"); +MODULE_DESCRIPTION("Aspeed AST2400/2500/2600 ADC Driver"); MODULE_LICENSE("GPL"); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:54 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:54 +0800 Subject: [v5 11/15] iio: adc: aspeed: Fix the calculate error of clock. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-12-billy_tsai@aspeedtech.com> The ADC clock formula is ast2400/2500: ADC clock period = PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) ast2600: ADC clock period = PCLK * 2 * (ADC0C[15:0] + 1) They all have one fixed divided 2 and the legacy driver didn't handle it. This patch register the fixed factory clock device as the parent of ADC clock scaler to fix this issue. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 40b7ba58c1dc..349377b9fac0 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -4,6 +4,12 @@ * * Copyright (C) 2017 Google, Inc. * Copyright (C) 2021 Aspeed Technology Inc. + * + * ADC clock formula: + * Ast2400/Ast2500: + * clock period = period of PCLK * 2 * (ADC0C[31:17] + 1) * (ADC0C[9:0] + 1) + * Ast2600: + * clock period = period of PCLK * 2 * (ADC0C[15:0] + 1) */ #include @@ -85,6 +91,7 @@ struct aspeed_adc_data { struct regulator *regulator; void __iomem *base; spinlock_t clk_lock; + struct clk_hw *fixed_div_clk; struct clk_hw *clk_prescaler; struct clk_hw *clk_scaler; struct reset_control *rst; @@ -204,6 +211,13 @@ static void aspeed_adc_unregister_divider(void *data) clk_hw_unregister_divider(clk); } +static void aspeed_adc_unregister_fixed_divider(void *data) +{ + struct clk_hw *clk = data; + + clk_hw_unregister_fixed_factor(clk); +} + static void aspeed_adc_reset_assert(void *data) { struct reset_control *rst = data; @@ -328,6 +342,19 @@ static int aspeed_adc_probe(struct platform_device *pdev) spin_lock_init(&data->clk_lock); snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), "%s", of_clk_get_parent_name(pdev->dev.of_node, 0)); + snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-fixed-div", + data->model_data->model_name); + data->fixed_div_clk = clk_hw_register_fixed_factor( + &pdev->dev, clk_name, clk_parent_name, 0, 1, 2); + if (IS_ERR(data->fixed_div_clk)) + return PTR_ERR(data->fixed_div_clk); + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_unregister_fixed_divider, + data->clk_prescaler); + if (ret) + return ret; + snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), clk_name); if (data->model_data->need_prescaler) { snprintf(clk_name, ARRAY_SIZE(clk_name), "%s-prescaler", -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:58 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:58 +0800 Subject: [v5 15/15] iio: adc: aspeed: Get and set trimming data. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-16-billy_tsai@aspeedtech.com> The ADC controller has a trimming register for fine-tune the reference voltage. The trimming value comes from the OTP register which will be written during chip production. This patch will read this OTP value and configure it to the ADC register when the ADC controller probes and using dts property "aspeed,trim-data-valid" to determine whether to execute this flow. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index b594a723db2e..fddd7508099f 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -80,6 +82,11 @@ */ #define ASPEED_ADC_DEF_SAMPLING_RATE 65000 +struct aspeed_adc_trim_locate { + const unsigned int offset; + const unsigned int field; +}; + struct aspeed_adc_model_data { const char *model_name; unsigned int min_sampling_rate; // Hz @@ -90,6 +97,7 @@ struct aspeed_adc_model_data { bool bat_sense_sup; u8 scaler_bit_width; unsigned int num_channels; + const struct aspeed_adc_trim_locate *trim_locate; }; struct adc_gain { @@ -165,6 +173,44 @@ static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = { ASPEED_BAT_CHAN(7, 0x1E), }; +static int aspeed_adc_set_trim_data(struct iio_dev *indio_dev) +{ + struct device_node *syscon; + struct regmap *scu; + u32 scu_otp, trimming_val; + struct aspeed_adc_data *data = iio_priv(indio_dev); + + syscon = of_find_node_by_name(NULL, "syscon"); + if (syscon == NULL) { + dev_warn(data->dev, "Couldn't find syscon node\n"); + return -EOPNOTSUPP; + } + scu = syscon_node_to_regmap(syscon); + if (IS_ERR(scu)) { + dev_warn(data->dev, "Failed to get syscon regmap\n"); + return -EOPNOTSUPP; + } + if (data->model_data->trim_locate) { + if (regmap_read(scu, data->model_data->trim_locate->offset, + &scu_otp)) { + dev_warn(data->dev, + "Failed to get adc trimming data\n"); + trimming_val = 0x8; + } else { + trimming_val = + ((scu_otp) & + (data->model_data->trim_locate->field)) >> + __ffs(data->model_data->trim_locate->field); + } + dev_dbg(data->dev, + "trimming val = %d, offset = %08x, fields = %08x\n", + trimming_val, data->model_data->trim_locate->offset, + data->model_data->trim_locate->field); + writel(trimming_val, data->base + ASPEED_REG_COMPENSATION_TRIM); + } + return 0; +} + static int aspeed_adc_compensation(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -545,6 +591,13 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + if (of_find_property(data->dev->of_node, "aspeed,trim-data-valid", + NULL)) { + ret = aspeed_adc_set_trim_data(indio_dev); + if (ret) + return ret; + } + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", NULL)) { if (data->model_data->bat_sense_sup) { @@ -616,6 +669,21 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; } +static const struct aspeed_adc_trim_locate ast2500_adc_trim = { + .offset = 0x154, + .field = GENMASK(31, 28), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc0_trim = { + .offset = 0x5d0, + .field = GENMASK(3, 0), +}; + +static const struct aspeed_adc_trim_locate ast2600_adc1_trim = { + .offset = 0x5d0, + .field = GENMASK(7, 4), +}; + static const struct aspeed_adc_model_data ast2400_model_data = { .model_name = "ast2400-adc", .vref_fixed_mv = 2500, @@ -635,6 +703,7 @@ static const struct aspeed_adc_model_data ast2500_model_data = { .need_prescaler = true, .scaler_bit_width = 10, .num_channels = 16, + .trim_locate = &ast2500_adc_trim, }; static const struct aspeed_adc_model_data ast2600_adc0_model_data = { @@ -645,6 +714,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc0_trim, }; static const struct aspeed_adc_model_data ast2600_adc1_model_data = { @@ -655,6 +725,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, + .trim_locate = &ast2600_adc1_trim, }; static const struct of_device_id aspeed_adc_matches[] = { -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:57 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:57 +0800 Subject: [v5 14/15] iio: adc: aspeed: Support battery sensing. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-15-billy_tsai@aspeedtech.com> In ast2600, ADC integrate dividing circuit at last input channel for battery sensing. This patch use the dts property "battery-sensing" to enable this feature makes the last channel of each adc can tolerance higher voltage than reference voltage. The offset interface of ch7 will be separated when enabling the battery sensing mode. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 81 ++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 121c96de03bd..b594a723db2e 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -87,10 +87,16 @@ struct aspeed_adc_model_data { unsigned int vref_fixed_mv; bool wait_init_sequence; bool need_prescaler; + bool bat_sense_sup; u8 scaler_bit_width; unsigned int num_channels; }; +struct adc_gain { + u8 mult; + u8 div; +}; + struct aspeed_adc_data { struct device *dev; const struct aspeed_adc_model_data *model_data; @@ -104,6 +110,8 @@ struct aspeed_adc_data { int vref_mv; u32 sample_period_ns; int cv; + bool battery_sensing; + struct adc_gain battery_mode_gain; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -136,6 +144,27 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +#define ASPEED_BAT_CHAN(_idx, _data_reg_addr) { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = (_idx), \ + .address = (_data_reg_addr), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ +} +static const struct iio_chan_spec aspeed_adc_iio_bat_channels[] = { + ASPEED_CHAN(0, 0x10), + ASPEED_CHAN(1, 0x12), + ASPEED_CHAN(2, 0x14), + ASPEED_CHAN(3, 0x16), + ASPEED_CHAN(4, 0x18), + ASPEED_CHAN(5, 0x1A), + ASPEED_CHAN(6, 0x1C), + ASPEED_BAT_CHAN(7, 0x1E), +}; + static int aspeed_adc_compensation(struct iio_dev *indio_dev) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -204,14 +233,39 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 adc_engine_control_reg_val; switch (mask) { case IIO_CHAN_INFO_RAW: - *val = readw(data->base + chan->address); + if (data->battery_sensing && chan->channel == 7) { + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + writel(adc_engine_control_reg_val | + FIELD_PREP(ASPEED_ADC_CH7_MODE, + ASPEED_ADC_CH7_BAT) | + ASPEED_ADC_BAT_SENSING_ENABLE, + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable battery sensing mode need to wait some time for adc stable + * Experiment result is 1ms. + */ + mdelay(1); + *val = readw(data->base + chan->address); + *val = (*val * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + /* Restore control register value */ + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + } else + *val = readw(data->base + chan->address); return IIO_VAL_INT; case IIO_CHAN_INFO_OFFSET: - *val = data->cv; + if (data->battery_sensing && chan->channel == 7) + *val = (data->cv * data->battery_mode_gain.mult) / + data->battery_mode_gain.div; + else + *val = data->cv; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: @@ -491,6 +545,23 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + if (of_find_property(data->dev->of_node, "aspeed,battery-sensing", + NULL)) { + if (data->model_data->bat_sense_sup) { + data->battery_sensing = 1; + if (readl(data->base + ASPEED_REG_ENGINE_CONTROL) & + ASPEED_ADC_BAT_SENSING_DIV) { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 1; + } else { + data->battery_mode_gain.mult = 3; + data->battery_mode_gain.div = 2; + } + } else + dev_warn(&pdev->dev, + "Failed to enable battey-sensing mode\n"); + } + if (data->model_data->wait_init_sequence) { adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); @@ -536,7 +607,9 @@ static int aspeed_adc_probe(struct platform_device *pdev) indio_dev->name = data->model_data->model_name; indio_dev->info = &aspeed_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = aspeed_adc_iio_channels; + indio_dev->channels = data->battery_sensing ? + aspeed_adc_iio_bat_channels : + aspeed_adc_iio_channels; indio_dev->num_channels = data->model_data->num_channels; ret = devm_iio_device_register(data->dev, indio_dev); @@ -569,6 +642,7 @@ static const struct aspeed_adc_model_data ast2600_adc0_model_data = { .min_sampling_rate = 10000, .max_sampling_rate = 500000, .wait_init_sequence = true, + .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, }; @@ -578,6 +652,7 @@ static const struct aspeed_adc_model_data ast2600_adc1_model_data = { .min_sampling_rate = 10000, .max_sampling_rate = 500000, .wait_init_sequence = true, + .bat_sense_sup = true, .scaler_bit_width = 16, .num_channels = 8, }; -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:56 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:56 +0800 Subject: [v5 13/15] iio: adc: aspeed: Add compensation phase. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-14-billy_tsai@aspeedtech.com> This patch adds a compensation phase to improve the accuracy of ADC measurement. This is the built-in function through input half of the reference voltage to get the ADC offset. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 54 +++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 1333d7a88427..121c96de03bd 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -103,6 +103,7 @@ struct aspeed_adc_data { struct reset_control *rst; int vref_mv; u32 sample_period_ns; + int cv; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -112,7 +113,8 @@ struct aspeed_adc_data { .address = (_data_reg_addr), \ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ - BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ } static const struct iio_chan_spec aspeed_adc_iio_channels[] = { @@ -134,6 +136,51 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_compensation(struct iio_dev *indio_dev) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + u32 index, adc_raw = 0; + u32 adc_engine_control_reg_val; + + adc_engine_control_reg_val = + readl(data->base + ASPEED_REG_ENGINE_CONTROL); + adc_engine_control_reg_val &= ~ASPEED_ADC_OP_MODE; + adc_engine_control_reg_val |= + (FIELD_PREP(ASPEED_ADC_OP_MODE, ASPEED_ADC_OP_MODE_NORMAL) | + ASPEED_ADC_ENGINE_ENABLE); + /* + * Enable compensating sensing: + * After that, the input voltage of ADC will force to half of the reference + * voltage. So the expected reading raw data will become half of the max + * value. We can get compensating value = 0x200 - ADC read raw value. + * It is recommended to average at least 10 samples to get a final CV. + */ + writel(adc_engine_control_reg_val | ASPEED_ADC_CTRL_COMPENSATION | + ASPEED_ADC_CTRL_CHANNEL_ENABLE(0), + data->base + ASPEED_REG_ENGINE_CONTROL); + /* + * After enable compensating sensing mode need to wait some time for ADC stable + * Experiment result is 1ms. + */ + mdelay(1); + + for (index = 0; index < 16; index++) { + /* + * Waiting for the sampling period ensures that the value acquired + * is fresh each time. + */ + ndelay(data->sample_period_ns); + adc_raw += readw(data->base + aspeed_adc_iio_channels[0].address); + } + adc_raw >>= 4; + data->cv = BIT(ASPEED_RESOLUTION_BITS - 1) - adc_raw; + writel(adc_engine_control_reg_val, + data->base + ASPEED_REG_ENGINE_CONTROL); + dev_dbg(data->dev, "Compensating value = %d\n", data->cv); + + return 0; +} + static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) { struct aspeed_adc_data *data = iio_priv(indio_dev); @@ -163,6 +210,10 @@ static int aspeed_adc_read_raw(struct iio_dev *indio_dev, *val = readw(data->base + chan->address); return IIO_VAL_INT; + case IIO_CHAN_INFO_OFFSET: + *val = data->cv; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: *val = data->vref_mv; *val2 = ASPEED_RESOLUTION_BITS; @@ -466,6 +517,7 @@ static int aspeed_adc_probe(struct platform_device *pdev) return ret; } + aspeed_adc_compensation(indio_dev); /* Start all channels in normal mode. */ adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); -- 2.25.1 From billy_tsai at aspeedtech.com Tue Aug 31 17:14:55 2021 From: billy_tsai at aspeedtech.com (Billy Tsai) Date: Tue, 31 Aug 2021 15:14:55 +0800 Subject: [v5 12/15] iio: adc: aspeed: Add func to set sampling rate. In-Reply-To: <20210831071458.2334-1-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> Message-ID: <20210831071458.2334-13-billy_tsai@aspeedtech.com> Add the function to set the sampling rate and keep the sampling period for a driver used to wait the fresh value. In addition, since the ADC clock is required when initializing the ADC device, move clk_prepare_enable ahead of the initialization phase. Signed-off-by: Billy Tsai --- drivers/iio/adc/aspeed_adc.c | 59 ++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 19 deletions(-) diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c index 349377b9fac0..1333d7a88427 100644 --- a/drivers/iio/adc/aspeed_adc.c +++ b/drivers/iio/adc/aspeed_adc.c @@ -73,6 +73,12 @@ #define ASPEED_ADC_INIT_POLLING_TIME 500 #define ASPEED_ADC_INIT_TIMEOUT 500000 +/* + * When the sampling rate is too high, the ADC may not have enough charging + * time, resulting in a low voltage value. Thus, the default uses a slow + * sampling rate for most use cases. + */ +#define ASPEED_ADC_DEF_SAMPLING_RATE 65000 struct aspeed_adc_model_data { const char *model_name; @@ -96,6 +102,7 @@ struct aspeed_adc_data { struct clk_hw *clk_scaler; struct reset_control *rst; int vref_mv; + u32 sample_period_ns; }; #define ASPEED_CHAN(_idx, _data_reg_addr) { \ @@ -127,6 +134,24 @@ static const struct iio_chan_spec aspeed_adc_iio_channels[] = { ASPEED_CHAN(15, 0x2E), }; +static int aspeed_adc_set_sampling_rate(struct iio_dev *indio_dev, u32 rate) +{ + struct aspeed_adc_data *data = iio_priv(indio_dev); + + if (rate < data->model_data->min_sampling_rate || + rate > data->model_data->max_sampling_rate) + return -EINVAL; + /* Each sampling needs 12 clocks to convert.*/ + clk_set_rate(data->clk_scaler->clk, rate * ASPEED_CLOCKS_PER_SAMPLE); + rate = clk_get_rate(data->clk_scaler->clk); + data->sample_period_ns = DIV_ROUND_UP_ULL( + (u64)NSEC_PER_SEC * ASPEED_CLOCKS_PER_SAMPLE, rate); + dev_dbg(data->dev, "Adc clock = %d sample period = %d ns", rate, + data->sample_period_ns); + + return 0; +} + static int aspeed_adc_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long mask) @@ -157,17 +182,9 @@ static int aspeed_adc_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) { - struct aspeed_adc_data *data = iio_priv(indio_dev); - switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val < data->model_data->min_sampling_rate || - val > data->model_data->max_sampling_rate) - return -EINVAL; - - clk_set_rate(data->clk_scaler->clk, - val * ASPEED_CLOCKS_PER_SAMPLE); - return 0; + return aspeed_adc_set_sampling_rate(indio_dev, val); case IIO_CHAN_INFO_SCALE: case IIO_CHAN_INFO_RAW: @@ -409,6 +426,20 @@ static int aspeed_adc_probe(struct platform_device *pdev) if (ret) return ret; + ret = clk_prepare_enable(data->clk_scaler->clk); + if (ret) + return ret; + + ret = devm_add_action_or_reset(data->dev, + aspeed_adc_clk_disable_unprepare, + data->clk_scaler->clk); + if (ret) + return ret; + + ret = aspeed_adc_set_sampling_rate(indio_dev, ASPEED_ADC_DEF_SAMPLING_RATE); + if (ret) + return ret; + if (data->model_data->wait_init_sequence) { adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); @@ -436,16 +467,6 @@ static int aspeed_adc_probe(struct platform_device *pdev) } /* Start all channels in normal mode. */ - ret = clk_prepare_enable(data->clk_scaler->clk); - if (ret) - return ret; - - ret = devm_add_action_or_reset(data->dev, - aspeed_adc_clk_disable_unprepare, - data->clk_scaler->clk); - if (ret) - return ret; - adc_engine_control_reg_val = readl(data->base + ASPEED_REG_ENGINE_CONTROL); adc_engine_control_reg_val |= -- 2.25.1 From caihuoqing at baidu.com Tue Aug 31 17:44:49 2021 From: caihuoqing at baidu.com (Cai Huoqing) Date: Tue, 31 Aug 2021 15:44:49 +0800 Subject: [PATCH] drm/aspeed: Make use of the helper function devm_platform_ioremap_resource() Message-ID: <20210831074449.563-1-caihuoqing@baidu.com> Use the devm_platform_ioremap_resource() helper instead of calling platform_get_resource() and devm_ioremap_resource() separately Signed-off-by: Cai Huoqing --- drivers/gpu/drm/aspeed/aspeed_gfx_drv.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c index b53fee6f1c17..de65da467de7 100644 --- a/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c +++ b/drivers/gpu/drm/aspeed/aspeed_gfx_drv.c @@ -133,11 +133,9 @@ static int aspeed_gfx_load(struct drm_device *drm) struct device_node *np = pdev->dev.of_node; const struct aspeed_gfx_config *config; const struct of_device_id *match; - struct resource *res; int ret; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - priv->base = devm_ioremap_resource(drm->dev, res); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); -- 2.25.1 From p.zabel at pengutronix.de Tue Aug 31 17:51:23 2021 From: p.zabel at pengutronix.de (Philipp Zabel) Date: Tue, 31 Aug 2021 09:51:23 +0200 Subject: [v5 09/15] iio: adc: aspeed: Use devm_add_action_or_reset. In-Reply-To: <20210831071458.2334-10-billy_tsai@aspeedtech.com> References: <20210831071458.2334-1-billy_tsai@aspeedtech.com> <20210831071458.2334-10-billy_tsai@aspeedtech.com> Message-ID: <4bc2bf196efd540933e6a781a44af20142e3d5d3.camel@pengutronix.de> Hi Billy, On Tue, 2021-08-31 at 15:14 +0800, Billy Tsai wrote: > This patch use devm_add_action_or_reset to handle the error in probe > phase. > > Signed-off-by: Billy Tsai > --- > drivers/iio/adc/aspeed_adc.c | 112 +++++++++++++++++++++-------------- > 1 file changed, 66 insertions(+), 46 deletions(-) > > diff --git a/drivers/iio/adc/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c > index e53d1541ad1d..af00d9752c1e 100644 > --- a/drivers/iio/adc/aspeed_adc.c > +++ b/drivers/iio/adc/aspeed_adc.c [...] > @@ -242,6 +271,11 @@ static int aspeed_adc_probe(struct platform_device *pdev) > &data->clk_lock); > if (IS_ERR(data->clk_prescaler)) > return PTR_ERR(data->clk_prescaler); > + ret = devm_add_action_or_reset(data->dev, > + aspeed_adc_unregister_divider, > + data->clk_prescaler); > + if (ret) > + return ret; I think here you could switch to devm_clk_hw_register_divider() instead. > snprintf(clk_parent_name, ARRAY_SIZE(clk_parent_name), > clk_name); > scaler_flags = CLK_SET_RATE_PARENT; > @@ -256,23 +290,30 @@ static int aspeed_adc_probe(struct platform_device *pdev) > &pdev->dev, clk_name, clk_parent_name, scaler_flags, > data->base + ASPEED_REG_CLOCK_CONTROL, 0, > data->model_data->scaler_bit_width, 0, &data->clk_lock); > - if (IS_ERR(data->clk_scaler)) { > - ret = PTR_ERR(data->clk_scaler); > - goto scaler_error; > - } > + if (IS_ERR(data->clk_scaler)) > + return PTR_ERR(data->clk_scaler); > + > + ret = devm_add_action_or_reset(data->dev, aspeed_adc_unregister_divider, > + data->clk_scaler); > + if (ret) > + return ret; Same as above. [...] > @@ -281,6 +322,10 @@ static int aspeed_adc_probe(struct platform_device *pdev) > ASPEED_ADC_ENGINE_ENABLE, > data->base + ASPEED_REG_ENGINE_CONTROL); > > + ret = devm_add_action_or_reset(data->dev, aspeed_adc_power_down, > + data); > + if (ret) > + return ret; > /* Wait for initial sequence complete. */ > ret = readl_poll_timeout(data->base + ASPEED_REG_ENGINE_CONTROL, > adc_engine_control_reg_val, [...] > @@ -303,6 +354,11 @@ static int aspeed_adc_probe(struct platform_device *pdev) > ASPEED_ADC_ENGINE_ENABLE; > writel(adc_engine_control_reg_val, > data->base + ASPEED_REG_ENGINE_CONTROL); > + ret = devm_add_action_or_reset(data->dev, > + aspeed_adc_power_down, > + data); > + if (ret) > + return ret; Should this be if (!model_dta->wait_init_sequence) ? Otherwise it looks like you've already registered the same aspeed_adc_power_down action above. regards Philipp