Commit c2c23dc7 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'mfd-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd

Pull MFD updates from Lee Jones:
 "Core Framework:
   - Change MFD support status from Supported to Maintained

  New Drivers:
   - Add support for the Intel Platform Management Component Interface
     (PMCI)

  Removed Drivers:
   - HTC PASIC3 LED/DS1WM
   - Toshiba T7L66XB, TC6387XB and TC6393XB TMIO

  New Device Support:
   - Add support for N6000 Flash to Intel M10 BMC PMCI
   - Add support for Lenovo Yoga Tab 3 to Intel CHTWC PMIC

  New Functionality:
   - Provide Reset support to Syscon

  Fix-ups:
   - Explicitly provide missing include files
   - Pass platform type data/info via the SPI/I2C/DT registration
     strategy
   - Lots of DT documentation / adaptions
   - Replace scnprintf() with preferred sysfs_emit()
   - Remove unused / superfluous code
   - Fix some trivial whitesspace / spelling / grammatical issues
   - Replace pm_power_off with new and improved
     register_sys_off_handler() API

  Bug Fixes:
   - Reintroduce RK808-clkout registration - fixing Wi-Fi and Bluetooth
   - Repair the order of AXPxxx IRQ PEK RISE/FALL definitions
   - Refuse to build CS5535 on unsupported UML architectures
   - Fix memory leaks in error return paths
   - Prevent refcount leaks in error return paths"

* tag 'mfd-next-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd: (40 commits)
  dt-bindings: mfd: qcom,tcsr: Add compatible for IPQ5332
  dt-bindings: mfd: Add NXP BBNSM
  mfd: ntxec: Add version number for EC in Tolino Vision
  dt-bindings: mfd: syscon: Add mt8365-syscfg
  mfd: Remove toshiba tmio drivers
  mfd: arizona: Use pm_runtime_resume_and_get() to prevent refcnt leak
  mfd: syscon: Allow reset control for syscon devices
  dt-bindings: mfd/syscon: Add resets property
  dt-bindings: mfd: syscon: Add amd,pensando-elba-syscon compatible
  dt-bindings: mfd: qcom,tcsr: Add compatible for MSM8226
  mfd: simple-mfd-i2c: Fix incoherent comment regarding DT registration
  mfd: axp20x: Switch to the sys-off handler API
  mfd: core: Spelling s/compement/complement/
  mfd: max8925: Remove the unused function irq_to_max8925()
  mfd: qcom-pm8xxx: Remove set but unused variable 'rev'
  dt-bindings: mfd: syscon: Document GXP register compatible
  mfd: twl4030-power: Drop empty platform remove function
  mfd: twl: Fix TWL6032 phy vbus detection
  mfd: pcf50633-adc: Fix potential memleak in pcf50633_adc_async_read()
  MAINTAINERS: Move MFD from a Supported to Maintaied state
  ...
parents 06e1a81c 59c54c59
What: /sys/bus/spi/devices/.../bmc_version
What: /sys/bus/.../drivers/intel-m10-bmc/.../bmc_version
Date: June 2020
KernelVersion: 5.10
Contact: Xu Yilun <yilun.xu@intel.com>
......@@ -6,7 +6,7 @@ Description: Read only. Returns the hardware build version of Intel
MAX10 BMC chip.
Format: "0x%x".
What: /sys/bus/spi/devices/.../bmcfw_version
What: /sys/bus/.../drivers/intel-m10-bmc/.../bmcfw_version
Date: June 2020
KernelVersion: 5.10
Contact: Xu Yilun <yilun.xu@intel.com>
......@@ -14,7 +14,7 @@ Description: Read only. Returns the firmware version of Intel MAX10
BMC chip.
Format: "0x%x".
What: /sys/bus/spi/devices/.../mac_address
What: /sys/bus/.../drivers/intel-m10-bmc/.../mac_address
Date: January 2021
KernelVersion: 5.12
Contact: Russ Weight <russell.h.weight@intel.com>
......@@ -25,7 +25,7 @@ Description: Read only. Returns the first MAC address in a block
space.
Format: "%02x:%02x:%02x:%02x:%02x:%02x".
What: /sys/bus/spi/devices/.../mac_count
What: /sys/bus/.../drivers/intel-m10-bmc/.../mac_count
Date: January 2021
KernelVersion: 5.12
Contact: Russ Weight <russell.h.weight@intel.com>
......
......@@ -33,6 +33,9 @@ properties:
- description:
For implementations of the EC connected through RPMSG.
const: google,cros-ec-rpmsg
- description:
For implementations of the EC connected through UART.
const: google,cros-ec-uart
controller-data: true
......@@ -187,6 +190,15 @@ allOf:
properties:
mediatek,rpmsg-name: false
- if:
properties:
compatible:
not:
contains:
enum:
- google,cros-ec-rpmsg
- google,cros-ec-uart
then:
required:
- reg
- interrupts
......@@ -299,4 +311,12 @@ examples:
vdd-supply = <&pp3300_fp_mcu>;
};
};
# Example for UART
- |
serial {
cros-ec {
compatible = "google,cros-ec-uart";
};
};
...
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/mfd/nxp,bbnsm.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: NXP Battery-Backed Non-Secure Module
maintainers:
- Jacky Bai <ping.bai@nxp.com>
description: |
NXP BBNSM serves as non-volatile logic and storage for the system.
it Intergrates RTC & ON/OFF control.
The RTC can retain its state and continues counting even when the
main chip is power down. A time alarm is generated once the most
significant 32 bits of the real-time counter match the value in the
Time Alarm register.
The ON/OFF logic inside the BBNSM allows for connecting directly to
a PMIC or other voltage regulator device. both smart PMIC mode and
Dumb PMIC mode supported.
properties:
compatible:
items:
- enum:
- nxp,imx93-bbnsm
- const: syscon
- const: simple-mfd
reg:
maxItems: 1
rtc:
type: object
$ref: /schemas/rtc/rtc.yaml#
properties:
compatible:
enum:
- nxp,imx93-bbnsm-rtc
interrupts:
maxItems: 1
start-year: true
required:
- compatible
- interrupts
additionalProperties: false
pwrkey:
type: object
$ref: /schemas/input/input.yaml#
properties:
compatible:
enum:
- nxp,imx93-bbnsm-pwrkey
interrupts:
maxItems: 1
linux,code: true
required:
- compatible
- interrupts
additionalProperties: false
required:
- compatible
- reg
- rtc
- pwrkey
additionalProperties: false
examples:
- |
#include <dt-bindings/input/linux-event-codes.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
bbnsm: bbnsm@44440000 {
compatible = "nxp,imx93-bbnsm", "syscon", "simple-mfd";
reg = <0x44440000 0x10000>;
bbnsm_rtc: rtc {
compatible = "nxp,imx93-bbnsm-rtc";
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
};
bbnsm_pwrkey: pwrkey {
compatible = "nxp,imx93-bbnsm-pwrkey";
interrupts = <GIC_SPI 73 IRQ_TYPE_LEVEL_HIGH>;
linux,code = <KEY_POWER>;
};
};
......@@ -44,6 +44,7 @@ properties:
- qcom,pm8004
- qcom,pm8005
- qcom,pm8009
- qcom,pm8010
- qcom,pm8019
- qcom,pm8028
- qcom,pm8110
......@@ -55,6 +56,10 @@ properties:
- qcom,pm8350
- qcom,pm8350b
- qcom,pm8350c
- qcom,pm8550
- qcom,pm8550b
- qcom,pm8550ve
- qcom,pm8550vs
- qcom,pm8841
- qcom,pm8909
- qcom,pm8916
......@@ -71,10 +76,12 @@ properties:
- qcom,pmi8998
- qcom,pmk8002
- qcom,pmk8350
- qcom,pmk8550
- qcom,pmm8155au
- qcom,pmp8074
- qcom,pmr735a
- qcom,pmr735b
- qcom,pmr735d
- qcom,pms405
- qcom,pmx55
- qcom,pmx65
......
......@@ -28,9 +28,11 @@ properties:
- qcom,sm8150-tcsr
- qcom,tcsr-apq8064
- qcom,tcsr-apq8084
- qcom,tcsr-ipq5332
- qcom,tcsr-ipq6018
- qcom,tcsr-ipq8064
- qcom,tcsr-mdm9615
- qcom,tcsr-msm8226
- qcom,tcsr-msm8660
- qcom,tcsr-msm8916
- qcom,tcsr-msm8953
......
......@@ -38,6 +38,7 @@ properties:
- allwinner,sun8i-h3-system-controller
- allwinner,sun8i-v3s-system-controller
- allwinner,sun50i-a64-system-controller
- amd,pensando-elba-syscon
- brcm,cru-clkset
- freecom,fsg-cs2-system-controller
- fsl,imx93-aonmix-ns-syscfg
......@@ -46,10 +47,12 @@ properties:
- hisilicon,hi6220-sramctrl
- hisilicon,pcie-sas-subctrl
- hisilicon,peri-subctrl
- hpe,gxp-sysreg
- intel,lgm-syscon
- marvell,armada-3700-usb2-host-misc
- mediatek,mt8135-pctl-a-syscfg
- mediatek,mt8135-pctl-b-syscfg
- mediatek,mt8365-syscfg
- microchip,lan966x-cpu-syscon
- microchip,sparx5-cpu-syscon
- mstar,msc313-pmsleep
......@@ -81,6 +84,9 @@ properties:
on the device.
enum: [1, 2, 4, 8]
resets:
maxItems: 1
hwlocks:
maxItems: 1
description:
......
......@@ -10458,7 +10458,7 @@ S: Maintained
F: Documentation/ABI/testing/sysfs-driver-intel-m10-bmc
F: Documentation/hwmon/intel-m10-bmc-hwmon.rst
F: drivers/hwmon/intel-m10-bmc-hwmon.c
F: drivers/mfd/intel-m10-bmc.c
F: drivers/mfd/intel-m10-bmc*
F: include/linux/mfd/intel-m10-bmc.h
INTEL MENLOW THERMAL DRIVER
......@@ -14181,7 +14181,7 @@ F: drivers/media/i2c/mt9v111.c
MULTIFUNCTION DEVICES (MFD)
M: Lee Jones <lee@kernel.org>
S: Supported
S: Maintained
T: git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
F: Documentation/devicetree/bindings/mfd/
F: drivers/mfd/
......
......@@ -537,6 +537,7 @@ static int cht_wc_extcon_probe(struct platform_device *pdev)
cht_wc_extcon_set_5v_boost(ext, false);
break;
case INTEL_CHT_WC_LENOVO_YOGABOOK1:
case INTEL_CHT_WC_LENOVO_YT3_X90:
/* Do this first, as it may very well return -EPROBE_DEFER. */
ret = cht_wc_extcon_get_role_sw_and_regulator(ext);
if (ret)
......
......@@ -246,7 +246,7 @@ config FPGA_MGR_VERSAL_FPGA
config FPGA_M10_BMC_SEC_UPDATE
tristate "Intel MAX10 BMC Secure Update driver"
depends on MFD_INTEL_M10_BMC
depends on MFD_INTEL_M10_BMC_CORE
select FW_LOADER
select FW_UPLOAD
help
......
......@@ -14,6 +14,12 @@
#include <linux/platform_device.h>
#include <linux/slab.h>
struct m10bmc_sec;
struct m10bmc_sec_ops {
int (*rsu_status)(struct m10bmc_sec *sec);
};
struct m10bmc_sec {
struct device *dev;
struct intel_m10bmc *m10bmc;
......@@ -21,6 +27,7 @@ struct m10bmc_sec {
char *fw_name;
u32 fw_name_id;
bool cancel_request;
const struct m10bmc_sec_ops *ops;
};
static DEFINE_XARRAY_ALLOC(fw_upload_xa);
......@@ -31,6 +38,71 @@ static DEFINE_XARRAY_ALLOC(fw_upload_xa);
#define REH_MAGIC GENMASK(15, 0)
#define REH_SHA_NUM_BYTES GENMASK(31, 16)
static int m10bmc_sec_write(struct m10bmc_sec *sec, const u8 *buf, u32 offset, u32 size)
{
struct intel_m10bmc *m10bmc = sec->m10bmc;
unsigned int stride = regmap_get_reg_stride(m10bmc->regmap);
u32 write_count = size / stride;
u32 leftover_offset = write_count * stride;
u32 leftover_size = size - leftover_offset;
u32 leftover_tmp = 0;
int ret;
if (sec->m10bmc->flash_bulk_ops)
return sec->m10bmc->flash_bulk_ops->write(m10bmc, buf, offset, size);
if (WARN_ON_ONCE(stride > sizeof(leftover_tmp)))
return -EINVAL;
ret = regmap_bulk_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset,
buf + offset, write_count);
if (ret)
return ret;
/* If size is not aligned to stride, handle the remainder bytes with regmap_write() */
if (leftover_size) {
memcpy(&leftover_tmp, buf + leftover_offset, leftover_size);
ret = regmap_write(m10bmc->regmap, M10BMC_STAGING_BASE + offset + leftover_offset,
leftover_tmp);
if (ret)
return ret;
}
return 0;
}
static int m10bmc_sec_read(struct m10bmc_sec *sec, u8 *buf, u32 addr, u32 size)
{
struct intel_m10bmc *m10bmc = sec->m10bmc;
unsigned int stride = regmap_get_reg_stride(m10bmc->regmap);
u32 read_count = size / stride;
u32 leftover_offset = read_count * stride;
u32 leftover_size = size - leftover_offset;
u32 leftover_tmp;
int ret;
if (sec->m10bmc->flash_bulk_ops)
return sec->m10bmc->flash_bulk_ops->read(m10bmc, buf, addr, size);
if (WARN_ON_ONCE(stride > sizeof(leftover_tmp)))
return -EINVAL;
ret = regmap_bulk_read(m10bmc->regmap, addr, buf, read_count);
if (ret)
return ret;
/* If size is not aligned to stride, handle the remainder bytes with regmap_read() */
if (leftover_size) {
ret = regmap_read(m10bmc->regmap, addr + leftover_offset, &leftover_tmp);
if (ret)
return ret;
memcpy(buf + leftover_offset, &leftover_tmp, leftover_size);
}
return 0;
}
static ssize_t
show_root_entry_hash(struct device *dev, u32 exp_magic,
u32 prog_addr, u32 reh_addr, char *buf)
......@@ -38,11 +110,9 @@ show_root_entry_hash(struct device *dev, u32 exp_magic,
struct m10bmc_sec *sec = dev_get_drvdata(dev);
int sha_num_bytes, i, ret, cnt = 0;
u8 hash[REH_SHA384_SIZE];
unsigned int stride;
u32 magic;
stride = regmap_get_reg_stride(sec->m10bmc->regmap);
ret = m10bmc_raw_read(sec->m10bmc, prog_addr, &magic);
ret = m10bmc_sec_read(sec, (u8 *)&magic, prog_addr, sizeof(magic));
if (ret)
return ret;
......@@ -50,19 +120,16 @@ show_root_entry_hash(struct device *dev, u32 exp_magic,
return sysfs_emit(buf, "hash not programmed\n");
sha_num_bytes = FIELD_GET(REH_SHA_NUM_BYTES, magic) / 8;
if ((sha_num_bytes % stride) ||
(sha_num_bytes != REH_SHA256_SIZE &&
sha_num_bytes != REH_SHA384_SIZE)) {
if (sha_num_bytes != REH_SHA256_SIZE &&
sha_num_bytes != REH_SHA384_SIZE) {
dev_err(sec->dev, "%s bad sha num bytes %d\n", __func__,
sha_num_bytes);
return -EINVAL;
}
ret = regmap_bulk_read(sec->m10bmc->regmap, reh_addr,
hash, sha_num_bytes / stride);
ret = m10bmc_sec_read(sec, hash, reh_addr, sha_num_bytes);
if (ret) {
dev_err(dev, "failed to read root entry hash: %x cnt %x: %d\n",
reh_addr, sha_num_bytes / stride, ret);
dev_err(dev, "failed to read root entry hash\n");
return ret;
}
......@@ -73,16 +140,24 @@ show_root_entry_hash(struct device *dev, u32 exp_magic,
return cnt;
}
#define DEVICE_ATTR_SEC_REH_RO(_name, _magic, _prog_addr, _reh_addr) \
#define DEVICE_ATTR_SEC_REH_RO(_name) \
static ssize_t _name##_root_entry_hash_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ return show_root_entry_hash(dev, _magic, _prog_addr, _reh_addr, buf); } \
{ \
struct m10bmc_sec *sec = dev_get_drvdata(dev); \
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; \
\
return show_root_entry_hash(dev, csr_map->_name##_magic, \
csr_map->_name##_prog_addr, \
csr_map->_name##_reh_addr, \
buf); \
} \
static DEVICE_ATTR_RO(_name##_root_entry_hash)
DEVICE_ATTR_SEC_REH_RO(bmc, BMC_PROG_MAGIC, BMC_PROG_ADDR, BMC_REH_ADDR);
DEVICE_ATTR_SEC_REH_RO(sr, SR_PROG_MAGIC, SR_PROG_ADDR, SR_REH_ADDR);
DEVICE_ATTR_SEC_REH_RO(pr, PR_PROG_MAGIC, PR_PROG_ADDR, PR_REH_ADDR);
DEVICE_ATTR_SEC_REH_RO(bmc);
DEVICE_ATTR_SEC_REH_RO(sr);
DEVICE_ATTR_SEC_REH_RO(pr);
#define CSK_BIT_LEN 128U
#define CSK_32ARRAY_SIZE DIV_ROUND_UP(CSK_BIT_LEN, 32)
......@@ -90,27 +165,16 @@ DEVICE_ATTR_SEC_REH_RO(pr, PR_PROG_MAGIC, PR_PROG_ADDR, PR_REH_ADDR);
static ssize_t
show_canceled_csk(struct device *dev, u32 addr, char *buf)
{
unsigned int i, stride, size = CSK_32ARRAY_SIZE * sizeof(u32);
unsigned int i, size = CSK_32ARRAY_SIZE * sizeof(u32);
struct m10bmc_sec *sec = dev_get_drvdata(dev);
DECLARE_BITMAP(csk_map, CSK_BIT_LEN);
__le32 csk_le32[CSK_32ARRAY_SIZE];
u32 csk32[CSK_32ARRAY_SIZE];
int ret;
stride = regmap_get_reg_stride(sec->m10bmc->regmap);
if (size % stride) {
dev_err(sec->dev,
"CSK vector size (0x%x) not aligned to stride (0x%x)\n",
size, stride);
WARN_ON_ONCE(1);
return -EINVAL;
}
ret = regmap_bulk_read(sec->m10bmc->regmap, addr, csk_le32,
size / stride);
ret = m10bmc_sec_read(sec, (u8 *)&csk_le32, addr, size);
if (ret) {
dev_err(sec->dev, "failed to read CSK vector: %x cnt %x: %d\n",
addr, size / stride, ret);
dev_err(sec->dev, "failed to read CSK vector\n");
return ret;
}
......@@ -122,18 +186,25 @@ show_canceled_csk(struct device *dev, u32 addr, char *buf)
return bitmap_print_to_pagebuf(1, buf, csk_map, CSK_BIT_LEN);
}
#define DEVICE_ATTR_SEC_CSK_RO(_name, _addr) \
#define DEVICE_ATTR_SEC_CSK_RO(_name) \
static ssize_t _name##_canceled_csks_show(struct device *dev, \
struct device_attribute *attr, \
char *buf) \
{ return show_canceled_csk(dev, _addr, buf); } \
{ \
struct m10bmc_sec *sec = dev_get_drvdata(dev); \
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map; \
\
return show_canceled_csk(dev, \
csr_map->_name##_prog_addr + CSK_VEC_OFFSET, \
buf); \
} \
static DEVICE_ATTR_RO(_name##_canceled_csks)
#define CSK_VEC_OFFSET 0x34
DEVICE_ATTR_SEC_CSK_RO(bmc, BMC_PROG_ADDR + CSK_VEC_OFFSET);
DEVICE_ATTR_SEC_CSK_RO(sr, SR_PROG_ADDR + CSK_VEC_OFFSET);
DEVICE_ATTR_SEC_CSK_RO(pr, PR_PROG_ADDR + CSK_VEC_OFFSET);
DEVICE_ATTR_SEC_CSK_RO(bmc);
DEVICE_ATTR_SEC_CSK_RO(sr);
DEVICE_ATTR_SEC_CSK_RO(pr);
#define FLASH_COUNT_SIZE 4096 /* count stored as inverted bit vector */
......@@ -141,31 +212,21 @@ static ssize_t flash_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct m10bmc_sec *sec = dev_get_drvdata(dev);
unsigned int stride, num_bits;
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
unsigned int num_bits;
u8 *flash_buf;
int cnt, ret;
stride = regmap_get_reg_stride(sec->m10bmc->regmap);
num_bits = FLASH_COUNT_SIZE * 8;
if (FLASH_COUNT_SIZE % stride) {
dev_err(sec->dev,
"FLASH_COUNT_SIZE (0x%x) not aligned to stride (0x%x)\n",
FLASH_COUNT_SIZE, stride);
WARN_ON_ONCE(1);
return -EINVAL;
}
flash_buf = kmalloc(FLASH_COUNT_SIZE, GFP_KERNEL);
if (!flash_buf)
return -ENOMEM;
ret = regmap_bulk_read(sec->m10bmc->regmap, STAGING_FLASH_COUNT,
flash_buf, FLASH_COUNT_SIZE / stride);
ret = m10bmc_sec_read(sec, flash_buf, csr_map->rsu_update_counter,
FLASH_COUNT_SIZE);
if (ret) {
dev_err(sec->dev,
"failed to read flash count: %x cnt %x: %d\n",
STAGING_FLASH_COUNT, FLASH_COUNT_SIZE / stride, ret);
dev_err(sec->dev, "failed to read flash count\n");
goto exit_free;
}
cnt = num_bits - bitmap_weight((unsigned long *)flash_buf, num_bits);
......@@ -200,25 +261,94 @@ static const struct attribute_group *m10bmc_sec_attr_groups[] = {
static void log_error_regs(struct m10bmc_sec *sec, u32 doorbell)
{
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
u32 auth_result;
dev_err(sec->dev, "RSU error status: 0x%08x\n", doorbell);
dev_err(sec->dev, "Doorbell: 0x%08x\n", doorbell);
if (!m10bmc_sys_read(sec->m10bmc, M10BMC_AUTH_RESULT, &auth_result))
if (!m10bmc_sys_read(sec->m10bmc, csr_map->auth_result, &auth_result))
dev_err(sec->dev, "RSU auth result: 0x%08x\n", auth_result);
}
static int m10bmc_sec_n3000_rsu_status(struct m10bmc_sec *sec)
{
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
u32 doorbell;
int ret;
ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
if (ret)
return ret;
return FIELD_GET(DRBL_RSU_STATUS, doorbell);
}
static int m10bmc_sec_n6000_rsu_status(struct m10bmc_sec *sec)
{
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
u32 auth_result;
int ret;
ret = m10bmc_sys_read(sec->m10bmc, csr_map->auth_result, &auth_result);
if (ret)
return ret;
return FIELD_GET(AUTH_RESULT_RSU_STATUS, auth_result);
}
static bool rsu_status_ok(u32 status)
{
return (status == RSU_STAT_NORMAL ||
status == RSU_STAT_NIOS_OK ||
status == RSU_STAT_USER_OK ||
status == RSU_STAT_FACTORY_OK);
}
static bool rsu_progress_done(u32 progress)
{
return (progress == RSU_PROG_IDLE ||
progress == RSU_PROG_RSU_DONE);
}
static bool rsu_progress_busy(u32 progress)
{
return (progress == RSU_PROG_AUTHENTICATING ||
progress == RSU_PROG_COPYING ||
progress == RSU_PROG_UPDATE_CANCEL ||
progress == RSU_PROG_PROGRAM_KEY_HASH);
}
static int m10bmc_sec_progress_status(struct m10bmc_sec *sec, u32 *doorbell_reg,
u32 *progress, u32 *status)
{
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
int ret;
ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, doorbell_reg);
if (ret)
return ret;
ret = sec->ops->rsu_status(sec);
if (ret < 0)
return ret;
*status = ret;
*progress = rsu_prog(*doorbell_reg);
return 0;
}
static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec)
{
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
u32 doorbell;
int ret;
ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell);
ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
if (ret)
return FW_UPLOAD_ERR_RW_ERROR;
if (rsu_prog(doorbell) != RSU_PROG_IDLE &&
rsu_prog(doorbell) != RSU_PROG_RSU_DONE) {
if (!rsu_progress_done(rsu_prog(doorbell))) {
log_error_regs(sec, doorbell);
return FW_UPLOAD_ERR_BUSY;
}
......@@ -226,19 +356,15 @@ static enum fw_upload_err rsu_check_idle(struct m10bmc_sec *sec)
return FW_UPLOAD_ERR_NONE;
}
static inline bool rsu_start_done(u32 doorbell)
static inline bool rsu_start_done(u32 doorbell_reg, u32 progress, u32 status)
{
u32 status, progress;
if (doorbell & DRBL_RSU_REQUEST)
if (doorbell_reg & DRBL_RSU_REQUEST)
return false;
status = rsu_stat(doorbell);
if (status == RSU_STAT_ERASE_FAIL || status == RSU_STAT_WEAROUT)
return true;
progress = rsu_prog(doorbell);
if (progress != RSU_PROG_IDLE && progress != RSU_PROG_RSU_DONE)
if (!rsu_progress_done(progress))
return true;
return false;
......@@ -246,11 +372,12 @@ static inline bool rsu_start_done(u32 doorbell)
static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec)
{
u32 doorbell, status;
int ret;
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
u32 doorbell_reg, progress, status;
int ret, err;
ret = regmap_update_bits(sec->m10bmc->regmap,
M10BMC_SYS_BASE + M10BMC_DOORBELL,
csr_map->base + csr_map->doorbell,
DRBL_RSU_REQUEST | DRBL_HOST_STATUS,
DRBL_RSU_REQUEST |
FIELD_PREP(DRBL_HOST_STATUS,
......@@ -258,26 +385,25 @@ static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec)
if (ret)
return FW_UPLOAD_ERR_RW_ERROR;
ret = regmap_read_poll_timeout(sec->m10bmc->regmap,
M10BMC_SYS_BASE + M10BMC_DOORBELL,
doorbell,
rsu_start_done(doorbell),
NIOS_HANDSHAKE_INTERVAL_US,
NIOS_HANDSHAKE_TIMEOUT_US);
ret = read_poll_timeout(m10bmc_sec_progress_status, err,
err < 0 || rsu_start_done(doorbell_reg, progress, status),
NIOS_HANDSHAKE_INTERVAL_US,
NIOS_HANDSHAKE_TIMEOUT_US,
false,
sec, &doorbell_reg, &progress, &status);
if (ret == -ETIMEDOUT) {
log_error_regs(sec, doorbell);
log_error_regs(sec, doorbell_reg);
return FW_UPLOAD_ERR_TIMEOUT;
} else if (ret) {
} else if (err) {
return FW_UPLOAD_ERR_RW_ERROR;
}
status = rsu_stat(doorbell);
if (status == RSU_STAT_WEAROUT) {
dev_warn(sec->dev, "Excessive flash update count detected\n");
return FW_UPLOAD_ERR_WEAROUT;
} else if (status == RSU_STAT_ERASE_FAIL) {
log_error_regs(sec, doorbell);
log_error_regs(sec, doorbell_reg);
return FW_UPLOAD_ERR_HW_ERROR;
}
......@@ -286,11 +412,12 @@ static enum fw_upload_err rsu_update_init(struct m10bmc_sec *sec)
static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec)
{
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
unsigned long poll_timeout;
u32 doorbell, progress;
int ret;
ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell);
ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
if (ret)
return FW_UPLOAD_ERR_RW_ERROR;
......@@ -300,7 +427,7 @@ static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec)
if (time_after(jiffies, poll_timeout))
break;
ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell);
ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
if (ret)
return FW_UPLOAD_ERR_RW_ERROR;
}
......@@ -319,11 +446,12 @@ static enum fw_upload_err rsu_prog_ready(struct m10bmc_sec *sec)
static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec)
{
u32 doorbell;
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
u32 doorbell_reg, status;
int ret;
ret = regmap_update_bits(sec->m10bmc->regmap,
M10BMC_SYS_BASE + M10BMC_DOORBELL,
csr_map->base + csr_map->doorbell,
DRBL_HOST_STATUS,
FIELD_PREP(DRBL_HOST_STATUS,
HOST_STATUS_WRITE_DONE));
......@@ -331,68 +459,58 @@ static enum fw_upload_err rsu_send_data(struct m10bmc_sec *sec)
return FW_UPLOAD_ERR_RW_ERROR;
ret = regmap_read_poll_timeout(sec->m10bmc->regmap,
M10BMC_SYS_BASE + M10BMC_DOORBELL,
doorbell,
rsu_prog(doorbell) != RSU_PROG_READY,
csr_map->base + csr_map->doorbell,
doorbell_reg,
rsu_prog(doorbell_reg) != RSU_PROG_READY,
NIOS_HANDSHAKE_INTERVAL_US,
NIOS_HANDSHAKE_TIMEOUT_US);
if (ret == -ETIMEDOUT) {
log_error_regs(sec, doorbell);
log_error_regs(sec, doorbell_reg);
return FW_UPLOAD_ERR_TIMEOUT;
} else if (ret) {
return FW_UPLOAD_ERR_RW_ERROR;
}
switch (rsu_stat(doorbell)) {
case RSU_STAT_NORMAL:
case RSU_STAT_NIOS_OK:
case RSU_STAT_USER_OK:
case RSU_STAT_FACTORY_OK:
break;
default:
log_error_regs(sec, doorbell);
ret = sec->ops->rsu_status(sec);
if (ret < 0)
return ret;
status = ret;
if (!rsu_status_ok(status)) {
log_error_regs(sec, doorbell_reg);
return FW_UPLOAD_ERR_HW_ERROR;
}
return FW_UPLOAD_ERR_NONE;
}
static int rsu_check_complete(struct m10bmc_sec *sec, u32 *doorbell)
static int rsu_check_complete(struct m10bmc_sec *sec, u32 *doorbell_reg)
{
if (m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, doorbell))
u32 progress, status;
if (m10bmc_sec_progress_status(sec, doorbell_reg, &progress, &status))
return -EIO;
switch (rsu_stat(*doorbell)) {
case RSU_STAT_NORMAL:
case RSU_STAT_NIOS_OK:
case RSU_STAT_USER_OK:
case RSU_STAT_FACTORY_OK:
break;
default:
if (!rsu_status_ok(status))
return -EINVAL;
}
switch (rsu_prog(*doorbell)) {
case RSU_PROG_IDLE:
case RSU_PROG_RSU_DONE:
if (rsu_progress_done(progress))
return 0;
case RSU_PROG_AUTHENTICATING:
case RSU_PROG_COPYING:
case RSU_PROG_UPDATE_CANCEL:
case RSU_PROG_PROGRAM_KEY_HASH:
if (rsu_progress_busy(progress))
return -EAGAIN;
default:
return -EINVAL;
}
return -EINVAL;
}
static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec)
{
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
u32 doorbell;
int ret;
ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell);
ret = m10bmc_sys_read(sec->m10bmc, csr_map->doorbell, &doorbell);
if (ret)
return FW_UPLOAD_ERR_RW_ERROR;
......@@ -400,7 +518,7 @@ static enum fw_upload_err rsu_cancel(struct m10bmc_sec *sec)
return FW_UPLOAD_ERR_BUSY;
ret = regmap_update_bits(sec->m10bmc->regmap,
M10BMC_SYS_BASE + M10BMC_DOORBELL,
csr_map->base + csr_map->doorbell,
DRBL_HOST_STATUS,
FIELD_PREP(DRBL_HOST_STATUS,
HOST_STATUS_ABORT_RSU));
......@@ -421,39 +539,50 @@ static enum fw_upload_err m10bmc_sec_prepare(struct fw_upload *fwl,
if (!size || size > M10BMC_STAGING_SIZE)
return FW_UPLOAD_ERR_INVALID_SIZE;
if (sec->m10bmc->flash_bulk_ops)
if (sec->m10bmc->flash_bulk_ops->lock_write(sec->m10bmc))
return FW_UPLOAD_ERR_BUSY;
ret = rsu_check_idle(sec);
if (ret != FW_UPLOAD_ERR_NONE)
return ret;
goto unlock_flash;
ret = rsu_update_init(sec);
if (ret != FW_UPLOAD_ERR_NONE)
return ret;
goto unlock_flash;
ret = rsu_prog_ready(sec);
if (ret != FW_UPLOAD_ERR_NONE)
return ret;
goto unlock_flash;
if (sec->cancel_request)
return rsu_cancel(sec);
if (sec->cancel_request) {
ret = rsu_cancel(sec);
goto unlock_flash;
}
return FW_UPLOAD_ERR_NONE;
unlock_flash:
if (sec->m10bmc->flash_bulk_ops)
sec->m10bmc->flash_bulk_ops->unlock_write(sec->m10bmc);
return ret;
}
#define WRITE_BLOCK_SIZE 0x4000 /* Default write-block size is 0x4000 bytes */
static enum fw_upload_err m10bmc_sec_write(struct fw_upload *fwl, const u8 *data,
u32 offset, u32 size, u32 *written)
static enum fw_upload_err m10bmc_sec_fw_write(struct fw_upload *fwl, const u8 *data,
u32 offset, u32 size, u32 *written)
{
struct m10bmc_sec *sec = fwl->dd_handle;
u32 blk_size, doorbell, extra_offset;
unsigned int stride, extra = 0;
const struct m10bmc_csr_map *csr_map = sec->m10bmc->info->csr_map;
struct intel_m10bmc *m10bmc = sec->m10bmc;
u32 blk_size, doorbell;
int ret;
stride = regmap_get_reg_stride(sec->m10bmc->regmap);
if (sec->cancel_request)
return rsu_cancel(sec);
ret = m10bmc_sys_read(sec->m10bmc, M10BMC_DOORBELL, &doorbell);
ret = m10bmc_sys_read(m10bmc, csr_map->doorbell, &doorbell);
if (ret) {
return FW_UPLOAD_ERR_RW_ERROR;
} else if (rsu_prog(doorbell) != RSU_PROG_READY) {
......@@ -461,28 +590,12 @@ static enum fw_upload_err m10bmc_sec_write(struct fw_upload *fwl, const u8 *data
return FW_UPLOAD_ERR_HW_ERROR;
}
WARN_ON_ONCE(WRITE_BLOCK_SIZE % stride);
WARN_ON_ONCE(WRITE_BLOCK_SIZE % regmap_get_reg_stride(m10bmc->regmap));
blk_size = min_t(u32, WRITE_BLOCK_SIZE, size);
ret = regmap_bulk_write(sec->m10bmc->regmap,
M10BMC_STAGING_BASE + offset,
(void *)data + offset,
blk_size / stride);
ret = m10bmc_sec_write(sec, data, offset, blk_size);
if (ret)
return FW_UPLOAD_ERR_RW_ERROR;
/*
* If blk_size is not aligned to stride, then handle the extra
* bytes with regmap_write.
*/
if (blk_size % stride) {
extra_offset = offset + ALIGN_DOWN(blk_size, stride);
memcpy(&extra, (u8 *)(data + extra_offset), blk_size % stride);
ret = regmap_write(sec->m10bmc->regmap,
M10BMC_STAGING_BASE + extra_offset, extra);
if (ret)
return FW_UPLOAD_ERR_RW_ERROR;
}
*written = blk_size;
return FW_UPLOAD_ERR_NONE;
}
......@@ -539,16 +652,27 @@ static void m10bmc_sec_cleanup(struct fw_upload *fwl)
struct m10bmc_sec *sec = fwl->dd_handle;
(void)rsu_cancel(sec);
if (sec->m10bmc->flash_bulk_ops)
sec->m10bmc->flash_bulk_ops->unlock_write(sec->m10bmc);
}
static const struct fw_upload_ops m10bmc_ops = {
.prepare = m10bmc_sec_prepare,
.write = m10bmc_sec_write,
.write = m10bmc_sec_fw_write,
.poll_complete = m10bmc_sec_poll_complete,
.cancel = m10bmc_sec_cancel,
.cleanup = m10bmc_sec_cleanup,
};
static const struct m10bmc_sec_ops m10sec_n3000_ops = {
.rsu_status = m10bmc_sec_n3000_rsu_status,
};
static const struct m10bmc_sec_ops m10sec_n6000_ops = {
.rsu_status = m10bmc_sec_n6000_rsu_status,
};
#define SEC_UPDATE_LEN_MAX 32
static int m10bmc_sec_probe(struct platform_device *pdev)
{
......@@ -564,6 +688,7 @@ static int m10bmc_sec_probe(struct platform_device *pdev)
sec->dev = &pdev->dev;
sec->m10bmc = dev_get_drvdata(pdev->dev.parent);
sec->ops = (struct m10bmc_sec_ops *)platform_get_device_id(pdev)->driver_data;
dev_set_drvdata(&pdev->dev, sec);
ret = xa_alloc(&fw_upload_xa, &sec->fw_name_id, sec,
......@@ -611,9 +736,15 @@ static int m10bmc_sec_remove(struct platform_device *pdev)
static const struct platform_device_id intel_m10bmc_sec_ids[] = {
{
.name = "n3000bmc-sec-update",
.driver_data = (kernel_ulong_t)&m10sec_n3000_ops,
},
{
.name = "d5005bmc-sec-update",
.driver_data = (kernel_ulong_t)&m10sec_n3000_ops,
},
{
.name = "n6000bmc-sec-update",
.driver_data = (kernel_ulong_t)&m10sec_n6000_ops,
},
{ }
};
......
......@@ -2340,7 +2340,7 @@ config SENSORS_XGENE
config SENSORS_INTEL_M10_BMC_HWMON
tristate "Intel MAX10 BMC Hardware Monitoring"
depends on MFD_INTEL_M10_BMC
depends on MFD_INTEL_M10_BMC_CORE
help
This driver provides support for the hardware monitoring functionality
on Intel MAX10 BMC chip.
......
......@@ -380,6 +380,49 @@ static struct i2c_board_info lenovo_yogabook1_board_info = {
.platform_data = &bq2589x_pdata,
};
/********** Lenovo Yogabook YT3-X90F charger settings **********/
static const char * const lenovo_yt3_bq25892_1_suppliers[] = { "cht_wcove_pwrsrc" };
/*
* bq25892 charger settings for the round li-ion cells in the hinge,
* this is the main / biggest battery.
*/
static const struct property_entry lenovo_yt3_bq25892_1_props[] = {
PROPERTY_ENTRY_STRING_ARRAY("supplied-from", lenovo_yt3_bq25892_1_suppliers),
PROPERTY_ENTRY_STRING("linux,secondary-charger-name", "bq25890-charger-0"),
PROPERTY_ENTRY_U32("linux,iinlim-percentage", 60),
PROPERTY_ENTRY_U32("linux,pump-express-vbus-max", 12000000),
PROPERTY_ENTRY_BOOL("linux,skip-reset"),
/*
* The firmware sets everything to the defaults, leading to a low(ish)
* charge-current and battery-voltage of 2048mA resp 4.2V. Use the
* Android values instead of "linux,read-back-settings" to fix this.
*/
PROPERTY_ENTRY_U32("ti,charge-current", 3072000),
PROPERTY_ENTRY_U32("ti,battery-regulation-voltage", 4352000),
PROPERTY_ENTRY_U32("ti,termination-current", 128000),
PROPERTY_ENTRY_U32("ti,precharge-current", 128000),
PROPERTY_ENTRY_U32("ti,minimum-sys-voltage", 3700000),
PROPERTY_ENTRY_BOOL("ti,use-ilim-pin"),
/* Set 5V boost current-limit to 1.2A (MAX/POR values are 2.45A/1.4A) */
PROPERTY_ENTRY_U32("ti,boost-voltage", 4998000),
PROPERTY_ENTRY_U32("ti,boost-max-current", 1200000),
{ }
};
static const struct software_node lenovo_yt3_bq25892_1_node = {
.properties = lenovo_yt3_bq25892_1_props,
};
/* bq25892 charger for the round li-ion cells in the hinge */
static struct i2c_board_info lenovo_yoga_tab3_board_info = {
.type = "bq25892",
.addr = 0x6b,
.dev_name = "bq25892_1",
.swnode = &lenovo_yt3_bq25892_1_node,
.platform_data = &bq2589x_pdata,
};
static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
{
struct intel_soc_pmic *pmic = dev_get_drvdata(pdev->dev.parent);
......@@ -459,6 +502,9 @@ static int cht_wc_i2c_adap_i2c_probe(struct platform_device *pdev)
case INTEL_CHT_WC_LENOVO_YOGABOOK1:
board_info = &lenovo_yogabook1_board_info;
break;
case INTEL_CHT_WC_LENOVO_YT3_X90:
board_info = &lenovo_yoga_tab3_board_info;
break;
default:
dev_warn(&pdev->dev, "Unknown model, not instantiating charger device\n");
break;
......
......@@ -15,6 +15,7 @@ config MFD_CS5535
tristate "AMD CS5535 and CS5536 southbridge core functions"
select MFD_CORE
depends on PCI && (X86_32 || (X86 && COMPILE_TEST))
depends on !UML
help
This is the core driver for CS5535/CS5536 MFD functions. This is
necessary for using the board's GPIO and MFGPT functionality.
......@@ -2166,14 +2167,32 @@ config SGI_MFD_IOC3
If you have an SGI Origin, Octane, or a PCI IOC3 card,
then say Y. Otherwise say N.
config MFD_INTEL_M10_BMC
tristate "Intel MAX 10 Board Management Controller"
depends on SPI_MASTER
select REGMAP_SPI_AVMM
select MFD_CORE
config MFD_INTEL_M10_BMC_CORE
tristate
select MFD_CORE
select REGMAP
default n
config MFD_INTEL_M10_BMC_SPI
tristate "Intel MAX 10 Board Management Controller with SPI"
depends on SPI_MASTER
select MFD_INTEL_M10_BMC_CORE
select REGMAP_SPI_AVMM
help
Support for the Intel MAX 10 board management controller using the
SPI interface.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
of the device.
config MFD_INTEL_M10_BMC_PMCI
tristate "Intel MAX 10 Board Management Controller with PMCI"
depends on FPGA_DFL
select MFD_INTEL_M10_BMC_CORE
select REGMAP
help
Support for the Intel MAX 10 board management controller using the
SPI interface.
Support for the Intel MAX 10 board management controller via PMCI.
This driver provides common support for accessing the device,
additional drivers must be enabled in order to use the functionality
......
......@@ -262,7 +262,10 @@ obj-$(CONFIG_MFD_QCOM_PM8008) += qcom-pm8008.o
obj-$(CONFIG_SGI_MFD_IOC3) += ioc3.o
obj-$(CONFIG_MFD_SIMPLE_MFD_I2C) += simple-mfd-i2c.o
obj-$(CONFIG_MFD_SMPRO) += smpro-core.o
obj-$(CONFIG_MFD_INTEL_M10_BMC) += intel-m10-bmc.o
obj-$(CONFIG_MFD_INTEL_M10_BMC_CORE) += intel-m10-bmc-core.o
obj-$(CONFIG_MFD_INTEL_M10_BMC_SPI) += intel-m10-bmc-spi.o
obj-$(CONFIG_MFD_INTEL_M10_BMC_PMCI) += intel-m10-bmc-pmci.o
obj-$(CONFIG_MFD_ATC260X) += atc260x-core.o
obj-$(CONFIG_MFD_ATC260X_I2C) += atc260x-i2c.o
......
......@@ -45,7 +45,7 @@ int arizona_clk32k_enable(struct arizona *arizona)
if (arizona->clk32k_ref == 1) {
switch (arizona->pdata.clk32k_src) {
case ARIZONA_32KZ_MCLK1:
ret = pm_runtime_get_sync(arizona->dev);
ret = pm_runtime_resume_and_get(arizona->dev);
if (ret != 0)
goto err_ref;
ret = clk_prepare_enable(arizona->mclk[ARIZONA_MCLK1]);
......
......@@ -23,7 +23,7 @@
#include <linux/mfd/core.h>
#include <linux/module.h>
#include <linux/of_device.h>
#include <linux/pm_runtime.h>
#include <linux/reboot.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
......@@ -825,17 +825,16 @@ static const struct mfd_cell axp813_cells[] = {
},
};
static struct axp20x_dev *axp20x_pm_power_off;
static void axp20x_power_off(void)
static int axp20x_power_off(struct sys_off_data *data)
{
if (axp20x_pm_power_off->variant == AXP288_ID)
return;
struct axp20x_dev *axp20x = data->cb_data;
regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
AXP20X_OFF);
regmap_write(axp20x->regmap, AXP20X_OFF_CTRL, AXP20X_OFF);
/* Give capacitors etc. time to drain to avoid kernel panic msg. */
mdelay(500);
return NOTIFY_DONE;
}
int axp20x_match_device(struct axp20x_dev *axp20x)
......@@ -1002,10 +1001,11 @@ int axp20x_device_probe(struct axp20x_dev *axp20x)
return ret;
}
if (!pm_power_off) {
axp20x_pm_power_off = axp20x;
pm_power_off = axp20x_power_off;
}
if (axp20x->variant != AXP288_ID)
devm_register_sys_off_handler(axp20x->dev,
SYS_OFF_MODE_POWER_OFF,
SYS_OFF_PRIO_DEFAULT,
axp20x_power_off, axp20x);
dev_info(axp20x->dev, "AXP20X driver loaded\n");
......@@ -1015,11 +1015,6 @@ EXPORT_SYMBOL(axp20x_device_probe);
void axp20x_device_remove(struct axp20x_dev *axp20x)
{
if (axp20x == axp20x_pm_power_off) {
axp20x_pm_power_off = NULL;
pm_power_off = NULL;
}
mfd_remove_devices(axp20x->dev);
regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc);
}
......
// SPDX-License-Identifier: GPL-2.0
/*
* Intel MAX 10 Board Management Controller chip - common code
*
* Copyright (C) 2018-2020 Intel Corporation. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/dev_printk.h>
#include <linux/mfd/core.h>
#include <linux/mfd/intel-m10-bmc.h>
#include <linux/module.h>
static ssize_t bmc_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = m10bmc_sys_read(ddata, ddata->info->csr_map->build_version, &val);
if (ret)
return ret;
return sprintf(buf, "0x%x\n", val);
}
static DEVICE_ATTR_RO(bmc_version);
static ssize_t bmcfw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = m10bmc_sys_read(ddata, ddata->info->csr_map->fw_version, &val);
if (ret)
return ret;
return sprintf(buf, "0x%x\n", val);
}
static DEVICE_ATTR_RO(bmcfw_version);
static ssize_t mac_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int macaddr_low, macaddr_high;
int ret;
ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_low, &macaddr_low);
if (ret)
return ret;
ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high);
if (ret)
return ret;
return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
(u8)FIELD_GET(M10BMC_N3000_MAC_BYTE1, macaddr_low),
(u8)FIELD_GET(M10BMC_N3000_MAC_BYTE2, macaddr_low),
(u8)FIELD_GET(M10BMC_N3000_MAC_BYTE3, macaddr_low),
(u8)FIELD_GET(M10BMC_N3000_MAC_BYTE4, macaddr_low),
(u8)FIELD_GET(M10BMC_N3000_MAC_BYTE5, macaddr_high),
(u8)FIELD_GET(M10BMC_N3000_MAC_BYTE6, macaddr_high));
}
static DEVICE_ATTR_RO(mac_address);
static ssize_t mac_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int macaddr_high;
int ret;
ret = m10bmc_sys_read(ddata, ddata->info->csr_map->mac_high, &macaddr_high);
if (ret)
return ret;
return sysfs_emit(buf, "%u\n", (u8)FIELD_GET(M10BMC_N3000_MAC_COUNT, macaddr_high));
}
static DEVICE_ATTR_RO(mac_count);
static struct attribute *m10bmc_attrs[] = {
&dev_attr_bmc_version.attr,
&dev_attr_bmcfw_version.attr,
&dev_attr_mac_address.attr,
&dev_attr_mac_count.attr,
NULL,
};
static const struct attribute_group m10bmc_group = {
.attrs = m10bmc_attrs,
};
const struct attribute_group *m10bmc_dev_groups[] = {
&m10bmc_group,
NULL,
};
EXPORT_SYMBOL_GPL(m10bmc_dev_groups);
int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platform_info *info)
{
int ret;
m10bmc->info = info;
dev_set_drvdata(m10bmc->dev, m10bmc);
ret = devm_mfd_add_devices(m10bmc->dev, PLATFORM_DEVID_AUTO,
info->cells, info->n_cells,
NULL, 0, NULL);
if (ret)
dev_err(m10bmc->dev, "Failed to register sub-devices: %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(m10bmc_dev_init);
MODULE_DESCRIPTION("Intel MAX 10 BMC core driver");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
// SPDX-License-Identifier: GPL-2.0
/*
* MAX10 BMC Platform Management Component Interface (PMCI) based
* interface.
*
* Copyright (C) 2020-2023 Intel Corporation.
*/
#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/dfl.h>
#include <linux/mfd/core.h>
#include <linux/mfd/intel-m10-bmc.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/regmap.h>
struct m10bmc_pmci_device {
void __iomem *base;
struct intel_m10bmc m10bmc;
struct mutex flash_mutex; /* protects flash_busy and serializes flash read/read */
bool flash_busy;
};
/*
* Intel FGPA indirect register access via hardware controller/bridge.
*/
#define INDIRECT_CMD_OFF 0
#define INDIRECT_CMD_CLR 0
#define INDIRECT_CMD_RD BIT(0)
#define INDIRECT_CMD_WR BIT(1)
#define INDIRECT_CMD_ACK BIT(2)
#define INDIRECT_ADDR_OFF 0x4
#define INDIRECT_RD_OFF 0x8
#define INDIRECT_WR_OFF 0xc
#define INDIRECT_INT_US 1
#define INDIRECT_TIMEOUT_US 10000
struct indirect_ctx {
void __iomem *base;
struct device *dev;
};
static int indirect_clear_cmd(struct indirect_ctx *ctx)
{
unsigned int cmd;
int ret;
writel(INDIRECT_CMD_CLR, ctx->base + INDIRECT_CMD_OFF);
ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, cmd,
cmd == INDIRECT_CMD_CLR,
INDIRECT_INT_US, INDIRECT_TIMEOUT_US);
if (ret)
dev_err(ctx->dev, "timed out waiting clear cmd (residual cmd=0x%x)\n", cmd);
return ret;
}
static int indirect_reg_read(void *context, unsigned int reg, unsigned int *val)
{
struct indirect_ctx *ctx = context;
unsigned int cmd, ack, tmpval;
int ret, ret2;
cmd = readl(ctx->base + INDIRECT_CMD_OFF);
if (cmd != INDIRECT_CMD_CLR)
dev_warn(ctx->dev, "residual cmd 0x%x on read entry\n", cmd);
writel(reg, ctx->base + INDIRECT_ADDR_OFF);
writel(INDIRECT_CMD_RD, ctx->base + INDIRECT_CMD_OFF);
ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, ack,
(ack & INDIRECT_CMD_ACK) == INDIRECT_CMD_ACK,
INDIRECT_INT_US, INDIRECT_TIMEOUT_US);
if (ret)
dev_err(ctx->dev, "read timed out on reg 0x%x ack 0x%x\n", reg, ack);
else
tmpval = readl(ctx->base + INDIRECT_RD_OFF);
ret2 = indirect_clear_cmd(ctx);
if (ret)
return ret;
if (ret2)
return ret2;
*val = tmpval;
return 0;
}
static int indirect_reg_write(void *context, unsigned int reg, unsigned int val)
{
struct indirect_ctx *ctx = context;
unsigned int cmd, ack;
int ret, ret2;
cmd = readl(ctx->base + INDIRECT_CMD_OFF);
if (cmd != INDIRECT_CMD_CLR)
dev_warn(ctx->dev, "residual cmd 0x%x on write entry\n", cmd);
writel(val, ctx->base + INDIRECT_WR_OFF);
writel(reg, ctx->base + INDIRECT_ADDR_OFF);
writel(INDIRECT_CMD_WR, ctx->base + INDIRECT_CMD_OFF);
ret = readl_poll_timeout(ctx->base + INDIRECT_CMD_OFF, ack,
(ack & INDIRECT_CMD_ACK) == INDIRECT_CMD_ACK,
INDIRECT_INT_US, INDIRECT_TIMEOUT_US);
if (ret)
dev_err(ctx->dev, "write timed out on reg 0x%x ack 0x%x\n", reg, ack);
ret2 = indirect_clear_cmd(ctx);
if (ret)
return ret;
return ret2;
}
static void pmci_write_fifo(void __iomem *base, const u32 *buf, size_t count)
{
while (count--)
writel(*buf++, base);
}
static void pmci_read_fifo(void __iomem *base, u32 *buf, size_t count)
{
while (count--)
*buf++ = readl(base);
}
static u32 pmci_get_write_space(struct m10bmc_pmci_device *pmci)
{
u32 val;
int ret;
ret = read_poll_timeout(readl, val,
FIELD_GET(M10BMC_N6000_FLASH_FIFO_SPACE, val) ==
M10BMC_N6000_FIFO_MAX_WORDS,
M10BMC_FLASH_INT_US, M10BMC_FLASH_TIMEOUT_US,
false, pmci->base + M10BMC_N6000_FLASH_CTRL);
if (ret == -ETIMEDOUT)
return 0;
return FIELD_GET(M10BMC_N6000_FLASH_FIFO_SPACE, val) * M10BMC_N6000_FIFO_WORD_SIZE;
}
static int pmci_flash_bulk_write(struct intel_m10bmc *m10bmc, const u8 *buf, u32 size)
{
struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc);
u32 blk_size, offset = 0, write_count;
while (size) {
blk_size = min(pmci_get_write_space(pmci), size);
if (blk_size == 0) {
dev_err(m10bmc->dev, "get FIFO available size fail\n");
return -EIO;
}
if (size < M10BMC_N6000_FIFO_WORD_SIZE)
break;
write_count = blk_size / M10BMC_N6000_FIFO_WORD_SIZE;
pmci_write_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO,
(u32 *)(buf + offset), write_count);
size -= blk_size;
offset += blk_size;
}
/* Handle remainder (less than M10BMC_N6000_FIFO_WORD_SIZE bytes) */
if (size) {
u32 tmp = 0;
memcpy(&tmp, buf + offset, size);
pmci_write_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO, &tmp, 1);
}
return 0;
}
static int pmci_flash_bulk_read(struct intel_m10bmc *m10bmc, u8 *buf, u32 addr, u32 size)
{
struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc);
u32 blk_size, offset = 0, val, full_read_count, read_count;
int ret;
while (size) {
blk_size = min_t(u32, size, M10BMC_N6000_READ_BLOCK_SIZE);
full_read_count = blk_size / M10BMC_N6000_FIFO_WORD_SIZE;
read_count = full_read_count;
if (full_read_count * M10BMC_N6000_FIFO_WORD_SIZE < blk_size)
read_count++;
writel(addr + offset, pmci->base + M10BMC_N6000_FLASH_ADDR);
writel(FIELD_PREP(M10BMC_N6000_FLASH_READ_COUNT, read_count) |
M10BMC_N6000_FLASH_RD_MODE,
pmci->base + M10BMC_N6000_FLASH_CTRL);
ret = readl_poll_timeout((pmci->base + M10BMC_N6000_FLASH_CTRL), val,
!(val & M10BMC_N6000_FLASH_BUSY),
M10BMC_FLASH_INT_US, M10BMC_FLASH_TIMEOUT_US);
if (ret) {
dev_err(m10bmc->dev, "read timed out on reading flash 0x%xn", val);
return ret;
}
pmci_read_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO,
(u32 *)(buf + offset), full_read_count);
size -= blk_size;
offset += blk_size;
if (full_read_count < read_count)
break;
writel(0, pmci->base + M10BMC_N6000_FLASH_CTRL);
}
/* Handle remainder (less than M10BMC_N6000_FIFO_WORD_SIZE bytes) */
if (size) {
u32 tmp;
pmci_read_fifo(pmci->base + M10BMC_N6000_FLASH_FIFO, &tmp, 1);
memcpy(buf + offset, &tmp, size);
writel(0, pmci->base + M10BMC_N6000_FLASH_CTRL);
}
return 0;
}
static int m10bmc_pmci_set_flash_host_mux(struct intel_m10bmc *m10bmc, bool request)
{
u32 ctrl;
int ret;
ret = regmap_update_bits(m10bmc->regmap, M10BMC_N6000_FLASH_MUX_CTRL,
M10BMC_N6000_FLASH_HOST_REQUEST,
FIELD_PREP(M10BMC_N6000_FLASH_HOST_REQUEST, request));
if (ret)
return ret;
return regmap_read_poll_timeout(m10bmc->regmap,
M10BMC_N6000_FLASH_MUX_CTRL, ctrl,
request ?
(get_flash_mux(ctrl) == M10BMC_N6000_FLASH_MUX_HOST) :
(get_flash_mux(ctrl) != M10BMC_N6000_FLASH_MUX_HOST),
M10BMC_FLASH_INT_US, M10BMC_FLASH_TIMEOUT_US);
}
static int m10bmc_pmci_flash_read(struct intel_m10bmc *m10bmc, u8 *buf, u32 addr, u32 size)
{
struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc);
int ret, ret2;
mutex_lock(&pmci->flash_mutex);
if (pmci->flash_busy) {
ret = -EBUSY;
goto unlock;
}
ret = m10bmc_pmci_set_flash_host_mux(m10bmc, true);
if (ret)
goto mux_fail;
ret = pmci_flash_bulk_read(m10bmc, buf, addr, size);
mux_fail:
ret2 = m10bmc_pmci_set_flash_host_mux(m10bmc, false);
unlock:
mutex_unlock(&pmci->flash_mutex);
if (ret)
return ret;
return ret2;
}
static int m10bmc_pmci_flash_write(struct intel_m10bmc *m10bmc, const u8 *buf, u32 offset, u32 size)
{
struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc);
int ret;
mutex_lock(&pmci->flash_mutex);
WARN_ON_ONCE(!pmci->flash_busy);
/* On write, firmware manages flash MUX */
ret = pmci_flash_bulk_write(m10bmc, buf + offset, size);
mutex_unlock(&pmci->flash_mutex);
return ret;
}
static int m10bmc_pmci_flash_lock(struct intel_m10bmc *m10bmc)
{
struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc);
int ret = 0;
mutex_lock(&pmci->flash_mutex);
if (pmci->flash_busy) {
ret = -EBUSY;
goto unlock;
}
pmci->flash_busy = true;
unlock:
mutex_unlock(&pmci->flash_mutex);
return ret;
}
static void m10bmc_pmci_flash_unlock(struct intel_m10bmc *m10bmc)
{
struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc);
mutex_lock(&pmci->flash_mutex);
WARN_ON_ONCE(!pmci->flash_busy);
pmci->flash_busy = false;
mutex_unlock(&pmci->flash_mutex);
}
static const struct intel_m10bmc_flash_bulk_ops m10bmc_pmci_flash_bulk_ops = {
.read = m10bmc_pmci_flash_read,
.write = m10bmc_pmci_flash_write,
.lock_write = m10bmc_pmci_flash_lock,
.unlock_write = m10bmc_pmci_flash_unlock,
};
static const struct regmap_range m10bmc_pmci_regmap_range[] = {
regmap_reg_range(M10BMC_N6000_SYS_BASE, M10BMC_N6000_SYS_END),
};
static const struct regmap_access_table m10bmc_pmci_access_table = {
.yes_ranges = m10bmc_pmci_regmap_range,
.n_yes_ranges = ARRAY_SIZE(m10bmc_pmci_regmap_range),
};
static struct regmap_config m10bmc_pmci_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
.wr_table = &m10bmc_pmci_access_table,
.rd_table = &m10bmc_pmci_access_table,
.reg_read = &indirect_reg_read,
.reg_write = &indirect_reg_write,
.max_register = M10BMC_N6000_SYS_END,
};
static struct mfd_cell m10bmc_pmci_n6000_bmc_subdevs[] = {
{ .name = "n6000bmc-hwmon" },
{ .name = "n6000bmc-sec-update" },
};
static const struct m10bmc_csr_map m10bmc_n6000_csr_map = {
.base = M10BMC_N6000_SYS_BASE,
.build_version = M10BMC_N6000_BUILD_VER,
.fw_version = NIOS2_N6000_FW_VERSION,
.mac_low = M10BMC_N6000_MAC_LOW,
.mac_high = M10BMC_N6000_MAC_HIGH,
.doorbell = M10BMC_N6000_DOORBELL,
.auth_result = M10BMC_N6000_AUTH_RESULT,
.bmc_prog_addr = M10BMC_N6000_BMC_PROG_ADDR,
.bmc_reh_addr = M10BMC_N6000_BMC_REH_ADDR,
.bmc_magic = M10BMC_N6000_BMC_PROG_MAGIC,
.sr_prog_addr = M10BMC_N6000_SR_PROG_ADDR,
.sr_reh_addr = M10BMC_N6000_SR_REH_ADDR,
.sr_magic = M10BMC_N6000_SR_PROG_MAGIC,
.pr_prog_addr = M10BMC_N6000_PR_PROG_ADDR,
.pr_reh_addr = M10BMC_N6000_PR_REH_ADDR,
.pr_magic = M10BMC_N6000_PR_PROG_MAGIC,
.rsu_update_counter = M10BMC_N6000_STAGING_FLASH_COUNT,
};
static const struct intel_m10bmc_platform_info m10bmc_pmci_n6000 = {
.cells = m10bmc_pmci_n6000_bmc_subdevs,
.n_cells = ARRAY_SIZE(m10bmc_pmci_n6000_bmc_subdevs),
.csr_map = &m10bmc_n6000_csr_map,
};
static int m10bmc_pmci_probe(struct dfl_device *ddev)
{
struct device *dev = &ddev->dev;
struct m10bmc_pmci_device *pmci;
struct indirect_ctx *ctx;
int ret;
pmci = devm_kzalloc(dev, sizeof(*pmci), GFP_KERNEL);
if (!pmci)
return -ENOMEM;
pmci->m10bmc.flash_bulk_ops = &m10bmc_pmci_flash_bulk_ops;
pmci->m10bmc.dev = dev;
pmci->base = devm_ioremap_resource(dev, &ddev->mmio_res);
if (IS_ERR(pmci->base))
return PTR_ERR(pmci->base);
ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
if (!ctx)
return -ENOMEM;
mutex_init(&pmci->flash_mutex);
ctx->base = pmci->base + M10BMC_N6000_INDIRECT_BASE;
ctx->dev = dev;
indirect_clear_cmd(ctx);
pmci->m10bmc.regmap = devm_regmap_init(dev, NULL, ctx, &m10bmc_pmci_regmap_config);
if (IS_ERR(pmci->m10bmc.regmap)) {
ret = PTR_ERR(pmci->m10bmc.regmap);
goto destroy_mutex;
}
ret = m10bmc_dev_init(&pmci->m10bmc, &m10bmc_pmci_n6000);
if (ret)
goto destroy_mutex;
return 0;
destroy_mutex:
mutex_destroy(&pmci->flash_mutex);
return ret;
}
static void m10bmc_pmci_remove(struct dfl_device *ddev)
{
struct intel_m10bmc *m10bmc = dev_get_drvdata(&ddev->dev);
struct m10bmc_pmci_device *pmci = container_of(m10bmc, struct m10bmc_pmci_device, m10bmc);
mutex_destroy(&pmci->flash_mutex);
}
#define FME_FEATURE_ID_M10BMC_PMCI 0x12
static const struct dfl_device_id m10bmc_pmci_ids[] = {
{ FME_ID, FME_FEATURE_ID_M10BMC_PMCI },
{ }
};
MODULE_DEVICE_TABLE(dfl, m10bmc_pmci_ids);
static struct dfl_driver m10bmc_pmci_driver = {
.drv = {
.name = "intel-m10-bmc",
.dev_groups = m10bmc_dev_groups,
},
.id_table = m10bmc_pmci_ids,
.probe = m10bmc_pmci_probe,
.remove = m10bmc_pmci_remove,
};
module_dfl_driver(m10bmc_pmci_driver);
MODULE_DESCRIPTION("MAX10 BMC PMCI-based interface");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL");
......@@ -5,39 +5,18 @@
* Copyright (C) 2018-2020 Intel Corporation. All rights reserved.
*/
#include <linux/bitfield.h>
#include <linux/dev_printk.h>
#include <linux/init.h>
#include <linux/mfd/core.h>
#include <linux/mfd/intel-m10-bmc.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
enum m10bmc_type {
M10_N3000,
M10_D5005,
M10_N5010,
};
static struct mfd_cell m10bmc_d5005_subdevs[] = {
{ .name = "d5005bmc-hwmon" },
{ .name = "d5005bmc-sec-update" }
};
static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
{ .name = "n3000bmc-hwmon" },
{ .name = "n3000bmc-retimer" },
{ .name = "n3000bmc-sec-update" },
};
static struct mfd_cell m10bmc_n5010_subdevs[] = {
{ .name = "n5010bmc-hwmon" },
};
static const struct regmap_range m10bmc_regmap_range[] = {
regmap_reg_range(M10BMC_LEGACY_BUILD_VER, M10BMC_LEGACY_BUILD_VER),
regmap_reg_range(M10BMC_SYS_BASE, M10BMC_SYS_END),
regmap_reg_range(M10BMC_FLASH_BASE, M10BMC_FLASH_END),
regmap_reg_range(M10BMC_N3000_LEGACY_BUILD_VER, M10BMC_N3000_LEGACY_BUILD_VER),
regmap_reg_range(M10BMC_N3000_SYS_BASE, M10BMC_N3000_SYS_END),
regmap_reg_range(M10BMC_N3000_FLASH_BASE, M10BMC_N3000_FLASH_END),
};
static const struct regmap_access_table m10bmc_access_table = {
......@@ -51,88 +30,8 @@ static struct regmap_config intel_m10bmc_regmap_config = {
.reg_stride = 4,
.wr_table = &m10bmc_access_table,
.rd_table = &m10bmc_access_table,
.max_register = M10BMC_MEM_END,
};
static ssize_t bmc_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = m10bmc_sys_read(ddata, M10BMC_BUILD_VER, &val);
if (ret)
return ret;
return sprintf(buf, "0x%x\n", val);
}
static DEVICE_ATTR_RO(bmc_version);
static ssize_t bmcfw_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *ddata = dev_get_drvdata(dev);
unsigned int val;
int ret;
ret = m10bmc_sys_read(ddata, NIOS2_FW_VERSION, &val);
if (ret)
return ret;
return sprintf(buf, "0x%x\n", val);
}
static DEVICE_ATTR_RO(bmcfw_version);
static ssize_t mac_address_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *max10 = dev_get_drvdata(dev);
unsigned int macaddr_low, macaddr_high;
int ret;
ret = m10bmc_sys_read(max10, M10BMC_MAC_LOW, &macaddr_low);
if (ret)
return ret;
ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high);
if (ret)
return ret;
return sysfs_emit(buf, "%02x:%02x:%02x:%02x:%02x:%02x\n",
(u8)FIELD_GET(M10BMC_MAC_BYTE1, macaddr_low),
(u8)FIELD_GET(M10BMC_MAC_BYTE2, macaddr_low),
(u8)FIELD_GET(M10BMC_MAC_BYTE3, macaddr_low),
(u8)FIELD_GET(M10BMC_MAC_BYTE4, macaddr_low),
(u8)FIELD_GET(M10BMC_MAC_BYTE5, macaddr_high),
(u8)FIELD_GET(M10BMC_MAC_BYTE6, macaddr_high));
}
static DEVICE_ATTR_RO(mac_address);
static ssize_t mac_count_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct intel_m10bmc *max10 = dev_get_drvdata(dev);
unsigned int macaddr_high;
int ret;
ret = m10bmc_sys_read(max10, M10BMC_MAC_HIGH, &macaddr_high);
if (ret)
return ret;
return sysfs_emit(buf, "%u\n",
(u8)FIELD_GET(M10BMC_MAC_COUNT, macaddr_high));
}
static DEVICE_ATTR_RO(mac_count);
static struct attribute *m10bmc_attrs[] = {
&dev_attr_bmc_version.attr,
&dev_attr_bmcfw_version.attr,
&dev_attr_mac_address.attr,
&dev_attr_mac_count.attr,
NULL,
.max_register = M10BMC_N3000_MEM_END,
};
ATTRIBUTE_GROUPS(m10bmc);
static int check_m10bmc_version(struct intel_m10bmc *ddata)
{
......@@ -142,16 +41,16 @@ static int check_m10bmc_version(struct intel_m10bmc *ddata)
/*
* This check is to filter out the very old legacy BMC versions. In the
* old BMC chips, the BMC version info is stored in the old version
* register (M10BMC_LEGACY_BUILD_VER), so its read out value would have
* not been M10BMC_VER_LEGACY_INVALID (0xffffffff). But in new BMC
* register (M10BMC_N3000_LEGACY_BUILD_VER), so its read out value would have
* not been M10BMC_N3000_VER_LEGACY_INVALID (0xffffffff). But in new BMC
* chips that the driver supports, the value of this register should be
* M10BMC_VER_LEGACY_INVALID.
* M10BMC_N3000_VER_LEGACY_INVALID.
*/
ret = m10bmc_raw_read(ddata, M10BMC_LEGACY_BUILD_VER, &v);
ret = m10bmc_raw_read(ddata, M10BMC_N3000_LEGACY_BUILD_VER, &v);
if (ret)
return -ENODEV;
if (v != M10BMC_VER_LEGACY_INVALID) {
if (v != M10BMC_N3000_VER_LEGACY_INVALID) {
dev_err(ddata->dev, "bad version M10BMC detected\n");
return -ENODEV;
}
......@@ -162,19 +61,19 @@ static int check_m10bmc_version(struct intel_m10bmc *ddata)
static int intel_m10_bmc_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
const struct intel_m10bmc_platform_info *info;
struct device *dev = &spi->dev;
struct mfd_cell *cells;
struct intel_m10bmc *ddata;
int ret, n_cell;
int ret;
ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
if (!ddata)
return -ENOMEM;
info = (struct intel_m10bmc_platform_info *)id->driver_data;
ddata->dev = dev;
ddata->regmap =
devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config);
ddata->regmap = devm_regmap_init_spi_avmm(spi, &intel_m10bmc_regmap_config);
if (IS_ERR(ddata->regmap)) {
ret = PTR_ERR(ddata->regmap);
dev_err(dev, "Failed to allocate regmap: %d\n", ret);
......@@ -189,35 +88,66 @@ static int intel_m10_bmc_spi_probe(struct spi_device *spi)
return ret;
}
switch (id->driver_data) {
case M10_N3000:
cells = m10bmc_pacn3000_subdevs;
n_cell = ARRAY_SIZE(m10bmc_pacn3000_subdevs);
break;
case M10_D5005:
cells = m10bmc_d5005_subdevs;
n_cell = ARRAY_SIZE(m10bmc_d5005_subdevs);
break;
case M10_N5010:
cells = m10bmc_n5010_subdevs;
n_cell = ARRAY_SIZE(m10bmc_n5010_subdevs);
break;
default:
return -ENODEV;
}
return m10bmc_dev_init(ddata, info);
}
ret = devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO, cells, n_cell,
NULL, 0, NULL);
if (ret)
dev_err(dev, "Failed to register sub-devices: %d\n", ret);
static const struct m10bmc_csr_map m10bmc_n3000_csr_map = {
.base = M10BMC_N3000_SYS_BASE,
.build_version = M10BMC_N3000_BUILD_VER,
.fw_version = NIOS2_N3000_FW_VERSION,
.mac_low = M10BMC_N3000_MAC_LOW,
.mac_high = M10BMC_N3000_MAC_HIGH,
.doorbell = M10BMC_N3000_DOORBELL,
.auth_result = M10BMC_N3000_AUTH_RESULT,
.bmc_prog_addr = M10BMC_N3000_BMC_PROG_ADDR,
.bmc_reh_addr = M10BMC_N3000_BMC_REH_ADDR,
.bmc_magic = M10BMC_N3000_BMC_PROG_MAGIC,
.sr_prog_addr = M10BMC_N3000_SR_PROG_ADDR,
.sr_reh_addr = M10BMC_N3000_SR_REH_ADDR,
.sr_magic = M10BMC_N3000_SR_PROG_MAGIC,
.pr_prog_addr = M10BMC_N3000_PR_PROG_ADDR,
.pr_reh_addr = M10BMC_N3000_PR_REH_ADDR,
.pr_magic = M10BMC_N3000_PR_PROG_MAGIC,
.rsu_update_counter = M10BMC_N3000_STAGING_FLASH_COUNT,
};
return ret;
}
static struct mfd_cell m10bmc_d5005_subdevs[] = {
{ .name = "d5005bmc-hwmon" },
{ .name = "d5005bmc-sec-update" },
};
static struct mfd_cell m10bmc_pacn3000_subdevs[] = {
{ .name = "n3000bmc-hwmon" },
{ .name = "n3000bmc-retimer" },
{ .name = "n3000bmc-sec-update" },
};
static struct mfd_cell m10bmc_n5010_subdevs[] = {
{ .name = "n5010bmc-hwmon" },
};
static const struct intel_m10bmc_platform_info m10bmc_spi_n3000 = {
.cells = m10bmc_pacn3000_subdevs,
.n_cells = ARRAY_SIZE(m10bmc_pacn3000_subdevs),
.csr_map = &m10bmc_n3000_csr_map,
};
static const struct intel_m10bmc_platform_info m10bmc_spi_d5005 = {
.cells = m10bmc_d5005_subdevs,
.n_cells = ARRAY_SIZE(m10bmc_d5005_subdevs),
.csr_map = &m10bmc_n3000_csr_map,
};
static const struct intel_m10bmc_platform_info m10bmc_spi_n5010 = {
.cells = m10bmc_n5010_subdevs,
.n_cells = ARRAY_SIZE(m10bmc_n5010_subdevs),
.csr_map = &m10bmc_n3000_csr_map,
};
static const struct spi_device_id m10bmc_spi_id[] = {
{ "m10-n3000", M10_N3000 },
{ "m10-d5005", M10_D5005 },
{ "m10-n5010", M10_N5010 },
{ "m10-n3000", (kernel_ulong_t)&m10bmc_spi_n3000 },
{ "m10-d5005", (kernel_ulong_t)&m10bmc_spi_d5005 },
{ "m10-n5010", (kernel_ulong_t)&m10bmc_spi_n5010 },
{ }
};
MODULE_DEVICE_TABLE(spi, m10bmc_spi_id);
......@@ -225,14 +155,14 @@ MODULE_DEVICE_TABLE(spi, m10bmc_spi_id);
static struct spi_driver intel_m10bmc_spi_driver = {
.driver = {
.name = "intel-m10-bmc",
.dev_groups = m10bmc_groups,
.dev_groups = m10bmc_dev_groups,
},
.probe = intel_m10_bmc_spi_probe,
.id_table = m10bmc_spi_id,
};
module_spi_driver(intel_m10bmc_spi_driver);
MODULE_DESCRIPTION("Intel MAX 10 BMC Device Driver");
MODULE_DESCRIPTION("Intel MAX 10 BMC SPI bus interface");
MODULE_AUTHOR("Intel Corporation");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("spi:intel-m10-bmc");
......@@ -165,6 +165,14 @@ static const struct dmi_system_id cht_wc_model_dmi_ids[] = {
/* Non exact match to match all versions */
DMI_MATCH(DMI_PRODUCT_NAME, "Lenovo YB1-X9"),
},
}, {
/* Lenovo Yoga Tab 3 Pro YT3-X90F */
.driver_data = (void *)(long)INTEL_CHT_WC_LENOVO_YT3_X90,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Intel Corporation"),
DMI_MATCH(DMI_PRODUCT_NAME, "CHERRYVIEW D1 PLATFORM"),
DMI_MATCH(DMI_PRODUCT_VERSION, "Blade3-10A-001"),
},
},
{ }
};
......
......@@ -349,7 +349,7 @@ static ssize_t pld_version_show(struct device *dev,
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%s\n", pld->info.version);
return sysfs_emit(buf, "%s\n", pld->info.version);
}
static ssize_t pld_specification_show(struct device *dev,
......@@ -357,8 +357,7 @@ static ssize_t pld_specification_show(struct device *dev,
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%d.%d\n", pld->info.spec_major,
pld->info.spec_minor);
return sysfs_emit(buf, "%d.%d\n", pld->info.spec_major, pld->info.spec_minor);
}
static ssize_t pld_type_show(struct device *dev,
......@@ -366,7 +365,7 @@ static ssize_t pld_type_show(struct device *dev,
{
struct kempld_device_data *pld = dev_get_drvdata(dev);
return scnprintf(buf, PAGE_SIZE, "%s\n", kempld_get_type_string(pld));
return sysfs_emit(buf, "%s\n", kempld_get_type_string(pld));
}
static DEVICE_ATTR_RO(pld_version);
......
......@@ -286,7 +286,7 @@ static ssize_t show_output(struct device *dev,
val = (val & mask) >> shift;
return scnprintf(buf, PAGE_SIZE, "%u\n", val);
return sysfs_emit(buf, "%u\n", val);
}
static ssize_t store_output(struct device *dev,
......
......@@ -469,12 +469,6 @@ static struct max8925_irq_data max8925_irqs[] = {
},
};
static inline struct max8925_irq_data *irq_to_max8925(struct max8925_chip *chip,
int irq)
{
return &max8925_irqs[irq - chip->irq_base];
}
static irqreturn_t max8925_irq(int irq, void *data)
{
struct max8925_chip *chip = data;
......
......@@ -175,6 +175,7 @@ static int ntxec_probe(struct i2c_client *client)
/* Bail out if we encounter an unknown firmware version */
switch (version) {
case NTXEC_VERSION_KOBO_AURA:
case NTXEC_VERSION_TOLINO_VISION:
subdevs = ntxec_subdev;
n_subdevs = ARRAY_SIZE(ntxec_subdev);
break;
......
......@@ -136,6 +136,7 @@ int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
void *callback_param)
{
struct pcf50633_adc_request *req;
int ret;
/* req is freed when the result is ready, in interrupt handler */
req = kmalloc(sizeof(*req), GFP_KERNEL);
......@@ -147,7 +148,11 @@ int pcf50633_adc_async_read(struct pcf50633 *pcf, int mux, int avg,
req->callback = callback;
req->callback_param = callback_param;
return adc_enqueue_request(pcf, req);
ret = adc_enqueue_request(pcf, req);
if (ret)
kfree(req);
return ret;
}
EXPORT_SYMBOL_GPL(pcf50633_adc_async_read);
......
......@@ -510,7 +510,6 @@ static int pm8xxx_probe(struct platform_device *pdev)
struct regmap *regmap;
int irq, rc;
unsigned int val;
u32 rev;
struct pm_irq_chip *chip;
data = of_device_get_match_data(&pdev->dev);
......@@ -535,7 +534,6 @@ static int pm8xxx_probe(struct platform_device *pdev)
return rc;
}
pr_info("PMIC revision 1: %02X\n", val);
rev = val;
/* Read PMIC chip revision 2 */
rc = regmap_read(regmap, REG_HWREV_2, &val);
......@@ -545,7 +543,6 @@ static int pm8xxx_probe(struct platform_device *pdev)
return rc;
}
pr_info("PMIC revision 2: %02X\n", val);
rev |= val << BITS_PER_BYTE;
chip = devm_kzalloc(&pdev->dev,
struct_size(chip, config, data->num_irqs),
......
......@@ -189,6 +189,7 @@ static const struct mfd_cell rk817s[] = {
};
static const struct mfd_cell rk818s[] = {
{ .name = "rk808-clkout", .id = PLATFORM_DEVID_NONE, },
{ .name = "rk808-regulator", .id = PLATFORM_DEVID_NONE, },
{
.name = "rk808-rtc",
......
......@@ -48,7 +48,7 @@ static int simple_mfd_i2c_probe(struct i2c_client *i2c)
if (IS_ERR(regmap))
return PTR_ERR(regmap);
/* If no MFD cells are spedified, use register the DT child nodes instead */
/* If no MFD cells are specified, register using the DT child nodes instead */
if (!simple_mfd_data || !simple_mfd_data->mfd_cell)
return devm_of_platform_populate(&i2c->dev);
......
......@@ -20,6 +20,7 @@
#include <linux/platform_data/syscon.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/mfd/syscon.h>
#include <linux/slab.h>
......@@ -31,6 +32,7 @@ static LIST_HEAD(syscon_list);
struct syscon {
struct device_node *np;
struct regmap *regmap;
struct reset_control *reset;
struct list_head list;
};
......@@ -40,7 +42,7 @@ static const struct regmap_config syscon_regmap_config = {
.reg_stride = 4,
};
static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
static struct syscon *of_syscon_register(struct device_node *np, bool check_res)
{
struct clk *clk;
struct syscon *syscon;
......@@ -50,6 +52,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
int ret;
struct regmap_config syscon_config = syscon_regmap_config;
struct resource res;
struct reset_control *reset;
syscon = kzalloc(sizeof(*syscon), GFP_KERNEL);
if (!syscon)
......@@ -114,7 +117,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
goto err_regmap;
}
if (check_clk) {
if (check_res) {
clk = of_clk_get(np, 0);
if (IS_ERR(clk)) {
ret = PTR_ERR(clk);
......@@ -124,8 +127,18 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
} else {
ret = regmap_mmio_attach_clk(regmap, clk);
if (ret)
goto err_attach;
goto err_attach_clk;
}
reset = of_reset_control_get_optional_exclusive(np, NULL);
if (IS_ERR(reset)) {
ret = PTR_ERR(reset);
goto err_attach_clk;
}
ret = reset_control_deassert(reset);
if (ret)
goto err_reset;
}
syscon->regmap = regmap;
......@@ -137,7 +150,9 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
return syscon;
err_attach:
err_reset:
reset_control_put(reset);
err_attach_clk:
if (!IS_ERR(clk))
clk_put(clk);
err_clk:
......@@ -150,7 +165,7 @@ static struct syscon *of_syscon_register(struct device_node *np, bool check_clk)
}
static struct regmap *device_node_get_regmap(struct device_node *np,
bool check_clk)
bool check_res)
{
struct syscon *entry, *syscon = NULL;
......@@ -165,7 +180,7 @@ static struct regmap *device_node_get_regmap(struct device_node *np,
spin_unlock(&syscon_list_slock);
if (!syscon)
syscon = of_syscon_register(np, check_clk);
syscon = of_syscon_register(np, check_res);
if (IS_ERR(syscon))
return ERR_CAST(syscon);
......
......@@ -110,8 +110,8 @@
#define TWL6030_BASEADD_PWM 0x00BA
#define TWL6030_BASEADD_GASGAUGE 0x00C0
#define TWL6030_BASEADD_PIH 0x00D0
#define TWL6030_BASEADD_CHARGER 0x00E0
#define TWL6032_BASEADD_CHARGER 0x00DA
#define TWL6030_BASEADD_CHARGER 0x00E0
#define TWL6030_BASEADD_LED 0x00F4
/* subchip/slave 2 0x4A - DFT */
......@@ -353,6 +353,9 @@ static struct twl_mapping twl6030_map[] = {
{ 2, TWL6030_BASEADD_ZERO },
{ 1, TWL6030_BASEADD_GPADC_CTRL },
{ 1, TWL6030_BASEADD_GASGAUGE },
/* TWL6032 specific charger registers */
{ 1, TWL6032_BASEADD_CHARGER },
};
static const struct regmap_config twl6030_regmap_config[3] = {
......@@ -803,10 +806,6 @@ twl_probe(struct i2c_client *client)
if ((id->driver_data) & TWL6030_CLASS) {
twl_priv->twl_id = TWL6030_CLASS_ID;
twl_priv->twl_map = &twl6030_map[0];
/* The charger base address is different in twl6032 */
if ((id->driver_data) & TWL6032_SUBCLASS)
twl_priv->twl_map[TWL_MODULE_MAIN_CHARGE].base =
TWL6032_BASEADD_CHARGER;
twl_regmap_config = twl6030_regmap_config;
} else {
twl_priv->twl_id = TWL4030_CLASS_ID;
......
......@@ -953,18 +953,12 @@ static int twl4030_power_probe(struct platform_device *pdev)
return err;
}
static int twl4030_power_remove(struct platform_device *pdev)
{
return 0;
}
static struct platform_driver twl4030_power_driver = {
.driver = {
.name = "twl4030_power",
.of_match_table = of_match_ptr(twl4030_power_of_match),
},
.probe = twl4030_power_probe,
.remove = twl4030_power_remove,
};
module_platform_driver(twl4030_power_driver);
......
......@@ -432,8 +432,9 @@ enum {
AXP152_IRQ_PEK_SHORT,
AXP152_IRQ_PEK_LONG,
AXP152_IRQ_TIMER,
AXP152_IRQ_PEK_RIS_EDGE,
/* out of bit order to make sure the press event is handled first */
AXP152_IRQ_PEK_FAL_EDGE,
AXP152_IRQ_PEK_RIS_EDGE,
AXP152_IRQ_GPIO3_INPUT,
AXP152_IRQ_GPIO2_INPUT,
AXP152_IRQ_GPIO1_INPUT,
......@@ -472,8 +473,9 @@ enum {
AXP20X_IRQ_LOW_PWR_LVL1,
AXP20X_IRQ_LOW_PWR_LVL2,
AXP20X_IRQ_TIMER,
AXP20X_IRQ_PEK_RIS_EDGE,
/* out of bit order to make sure the press event is handled first */
AXP20X_IRQ_PEK_FAL_EDGE,
AXP20X_IRQ_PEK_RIS_EDGE,
AXP20X_IRQ_GPIO3_INPUT,
AXP20X_IRQ_GPIO2_INPUT,
AXP20X_IRQ_GPIO1_INPUT,
......@@ -502,8 +504,9 @@ enum axp22x_irqs {
AXP22X_IRQ_LOW_PWR_LVL1,
AXP22X_IRQ_LOW_PWR_LVL2,
AXP22X_IRQ_TIMER,
AXP22X_IRQ_PEK_RIS_EDGE,
/* out of bit order to make sure the press event is handled first */
AXP22X_IRQ_PEK_FAL_EDGE,
AXP22X_IRQ_PEK_RIS_EDGE,
AXP22X_IRQ_GPIO1_INPUT,
AXP22X_IRQ_GPIO0_INPUT,
};
......@@ -571,8 +574,9 @@ enum axp803_irqs {
AXP803_IRQ_LOW_PWR_LVL1,
AXP803_IRQ_LOW_PWR_LVL2,
AXP803_IRQ_TIMER,
AXP803_IRQ_PEK_RIS_EDGE,
/* out of bit order to make sure the press event is handled first */
AXP803_IRQ_PEK_FAL_EDGE,
AXP803_IRQ_PEK_RIS_EDGE,
AXP803_IRQ_PEK_SHORT,
AXP803_IRQ_PEK_LONG,
AXP803_IRQ_PEK_OVER_OFF,
......@@ -623,8 +627,9 @@ enum axp809_irqs {
AXP809_IRQ_LOW_PWR_LVL1,
AXP809_IRQ_LOW_PWR_LVL2,
AXP809_IRQ_TIMER,
AXP809_IRQ_PEK_RIS_EDGE,
/* out of bit order to make sure the press event is handled first */
AXP809_IRQ_PEK_FAL_EDGE,
AXP809_IRQ_PEK_RIS_EDGE,
AXP809_IRQ_PEK_SHORT,
AXP809_IRQ_PEK_LONG,
AXP809_IRQ_PEK_OVER_OFF,
......
......@@ -88,7 +88,7 @@ struct mfd_cell {
const char *of_compatible;
/*
* Address as defined in Device Tree. Used to compement 'of_compatible'
* Address as defined in Device Tree. Used to complement 'of_compatible'
* (above) when matching OF nodes with devices that have identical
* compatible strings
*/
......
......@@ -7,40 +7,43 @@
#ifndef __MFD_INTEL_M10_BMC_H
#define __MFD_INTEL_M10_BMC_H
#include <linux/bitfield.h>
#include <linux/bits.h>
#include <linux/dev_printk.h>
#include <linux/regmap.h>
#define M10BMC_LEGACY_BUILD_VER 0x300468
#define M10BMC_SYS_BASE 0x300800
#define M10BMC_SYS_END 0x300fff
#define M10BMC_FLASH_BASE 0x10000000
#define M10BMC_FLASH_END 0x1fffffff
#define M10BMC_MEM_END M10BMC_FLASH_END
#define M10BMC_N3000_LEGACY_BUILD_VER 0x300468
#define M10BMC_N3000_SYS_BASE 0x300800
#define M10BMC_N3000_SYS_END 0x300fff
#define M10BMC_N3000_FLASH_BASE 0x10000000
#define M10BMC_N3000_FLASH_END 0x1fffffff
#define M10BMC_N3000_MEM_END M10BMC_N3000_FLASH_END
#define M10BMC_STAGING_BASE 0x18000000
#define M10BMC_STAGING_SIZE 0x3800000
/* Register offset of system registers */
#define NIOS2_FW_VERSION 0x0
#define M10BMC_MAC_LOW 0x10
#define M10BMC_MAC_BYTE4 GENMASK(7, 0)
#define M10BMC_MAC_BYTE3 GENMASK(15, 8)
#define M10BMC_MAC_BYTE2 GENMASK(23, 16)
#define M10BMC_MAC_BYTE1 GENMASK(31, 24)
#define M10BMC_MAC_HIGH 0x14
#define M10BMC_MAC_BYTE6 GENMASK(7, 0)
#define M10BMC_MAC_BYTE5 GENMASK(15, 8)
#define M10BMC_MAC_COUNT GENMASK(23, 16)
#define M10BMC_TEST_REG 0x3c
#define M10BMC_BUILD_VER 0x68
#define M10BMC_VER_MAJOR_MSK GENMASK(23, 16)
#define M10BMC_VER_PCB_INFO_MSK GENMASK(31, 24)
#define M10BMC_VER_LEGACY_INVALID 0xffffffff
#define NIOS2_N3000_FW_VERSION 0x0
#define M10BMC_N3000_MAC_LOW 0x10
#define M10BMC_N3000_MAC_BYTE4 GENMASK(7, 0)
#define M10BMC_N3000_MAC_BYTE3 GENMASK(15, 8)
#define M10BMC_N3000_MAC_BYTE2 GENMASK(23, 16)
#define M10BMC_N3000_MAC_BYTE1 GENMASK(31, 24)
#define M10BMC_N3000_MAC_HIGH 0x14
#define M10BMC_N3000_MAC_BYTE6 GENMASK(7, 0)
#define M10BMC_N3000_MAC_BYTE5 GENMASK(15, 8)
#define M10BMC_N3000_MAC_COUNT GENMASK(23, 16)
#define M10BMC_N3000_TEST_REG 0x3c
#define M10BMC_N3000_BUILD_VER 0x68
#define M10BMC_N3000_VER_MAJOR_MSK GENMASK(23, 16)
#define M10BMC_N3000_VER_PCB_INFO_MSK GENMASK(31, 24)
#define M10BMC_N3000_VER_LEGACY_INVALID 0xffffffff
/* Secure update doorbell register, in system register region */
#define M10BMC_DOORBELL 0x400
#define M10BMC_N3000_DOORBELL 0x400
/* Authorization Result register, in system register region */
#define M10BMC_AUTH_RESULT 0x404
#define M10BMC_N3000_AUTH_RESULT 0x404
/* Doorbell register fields */
#define DRBL_RSU_REQUEST BIT(0)
......@@ -88,7 +91,6 @@
#define HOST_STATUS_ABORT_RSU 0x2
#define rsu_prog(doorbell) FIELD_GET(DRBL_RSU_PROGRESS, doorbell)
#define rsu_stat(doorbell) FIELD_GET(DRBL_RSU_STATUS, doorbell)
/* interval 100ms and timeout 5s */
#define NIOS_HANDSHAKE_INTERVAL_US (100 * 1000)
......@@ -103,29 +105,145 @@
#define RSU_COMPLETE_TIMEOUT_MS (40 * 60 * 1000)
/* Addresses for security related data in FLASH */
#define BMC_REH_ADDR 0x17ffc004
#define BMC_PROG_ADDR 0x17ffc000
#define BMC_PROG_MAGIC 0x5746
#define M10BMC_N3000_BMC_REH_ADDR 0x17ffc004
#define M10BMC_N3000_BMC_PROG_ADDR 0x17ffc000
#define M10BMC_N3000_BMC_PROG_MAGIC 0x5746
#define SR_REH_ADDR 0x17ffd004
#define SR_PROG_ADDR 0x17ffd000
#define SR_PROG_MAGIC 0x5253
#define M10BMC_N3000_SR_REH_ADDR 0x17ffd004
#define M10BMC_N3000_SR_PROG_ADDR 0x17ffd000
#define M10BMC_N3000_SR_PROG_MAGIC 0x5253
#define PR_REH_ADDR 0x17ffe004
#define PR_PROG_ADDR 0x17ffe000
#define PR_PROG_MAGIC 0x5250
#define M10BMC_N3000_PR_REH_ADDR 0x17ffe004
#define M10BMC_N3000_PR_PROG_ADDR 0x17ffe000
#define M10BMC_N3000_PR_PROG_MAGIC 0x5250
/* Address of 4KB inverted bit vector containing staging area FLASH count */
#define STAGING_FLASH_COUNT 0x17ffb000
#define M10BMC_N3000_STAGING_FLASH_COUNT 0x17ffb000
#define M10BMC_N6000_INDIRECT_BASE 0x400
#define M10BMC_N6000_SYS_BASE 0x0
#define M10BMC_N6000_SYS_END 0xfff
#define M10BMC_N6000_DOORBELL 0x1c0
#define M10BMC_N6000_AUTH_RESULT 0x1c4
#define AUTH_RESULT_RSU_STATUS GENMASK(23, 16)
#define M10BMC_N6000_BUILD_VER 0x0
#define NIOS2_N6000_FW_VERSION 0x4
#define M10BMC_N6000_MAC_LOW 0x20
#define M10BMC_N6000_MAC_HIGH (M10BMC_N6000_MAC_LOW + 4)
/* Addresses for security related data in FLASH */
#define M10BMC_N6000_BMC_REH_ADDR 0x7ffc004
#define M10BMC_N6000_BMC_PROG_ADDR 0x7ffc000
#define M10BMC_N6000_BMC_PROG_MAGIC 0x5746
#define M10BMC_N6000_SR_REH_ADDR 0x7ffd004
#define M10BMC_N6000_SR_PROG_ADDR 0x7ffd000
#define M10BMC_N6000_SR_PROG_MAGIC 0x5253
#define M10BMC_N6000_PR_REH_ADDR 0x7ffe004
#define M10BMC_N6000_PR_PROG_ADDR 0x7ffe000
#define M10BMC_N6000_PR_PROG_MAGIC 0x5250
#define M10BMC_N6000_STAGING_FLASH_COUNT 0x7ff5000
#define M10BMC_N6000_FLASH_MUX_CTRL 0x1d0
#define M10BMC_N6000_FLASH_MUX_SELECTION GENMASK(2, 0)
#define M10BMC_N6000_FLASH_MUX_IDLE 0
#define M10BMC_N6000_FLASH_MUX_NIOS 1
#define M10BMC_N6000_FLASH_MUX_HOST 2
#define M10BMC_N6000_FLASH_MUX_PFL 4
#define get_flash_mux(mux) FIELD_GET(M10BMC_N6000_FLASH_MUX_SELECTION, mux)
#define M10BMC_N6000_FLASH_NIOS_REQUEST BIT(4)
#define M10BMC_N6000_FLASH_HOST_REQUEST BIT(5)
#define M10BMC_N6000_FLASH_CTRL 0x40
#define M10BMC_N6000_FLASH_WR_MODE BIT(0)
#define M10BMC_N6000_FLASH_RD_MODE BIT(1)
#define M10BMC_N6000_FLASH_BUSY BIT(2)
#define M10BMC_N6000_FLASH_FIFO_SPACE GENMASK(13, 4)
#define M10BMC_N6000_FLASH_READ_COUNT GENMASK(25, 16)
#define M10BMC_N6000_FLASH_ADDR 0x44
#define M10BMC_N6000_FLASH_FIFO 0x800
#define M10BMC_N6000_READ_BLOCK_SIZE 0x800
#define M10BMC_N6000_FIFO_MAX_BYTES 0x800
#define M10BMC_N6000_FIFO_WORD_SIZE 4
#define M10BMC_N6000_FIFO_MAX_WORDS (M10BMC_N6000_FIFO_MAX_BYTES / \
M10BMC_N6000_FIFO_WORD_SIZE)
#define M10BMC_FLASH_INT_US 1
#define M10BMC_FLASH_TIMEOUT_US 10000
/**
* struct m10bmc_csr_map - Intel MAX 10 BMC CSR register map
*/
struct m10bmc_csr_map {
unsigned int base;
unsigned int build_version;
unsigned int fw_version;
unsigned int mac_low;
unsigned int mac_high;
unsigned int doorbell;
unsigned int auth_result;
unsigned int bmc_prog_addr;
unsigned int bmc_reh_addr;
unsigned int bmc_magic;
unsigned int sr_prog_addr;
unsigned int sr_reh_addr;
unsigned int sr_magic;
unsigned int pr_prog_addr;
unsigned int pr_reh_addr;
unsigned int pr_magic;
unsigned int rsu_update_counter;
};
/**
* struct intel_m10bmc_platform_info - Intel MAX 10 BMC platform specific information
* @cells: MFD cells
* @n_cells: MFD cells ARRAY_SIZE()
* @csr_map: the mappings for register definition of MAX10 BMC
*/
struct intel_m10bmc_platform_info {
struct mfd_cell *cells;
int n_cells;
const struct m10bmc_csr_map *csr_map;
};
struct intel_m10bmc;
/**
* struct intel_m10bmc_flash_bulk_ops - device specific operations for flash R/W
* @read: read a block of data from flash
* @write: write a block of data to flash
* @lock_write: locks flash access for erase+write
* @unlock_write: unlock flash access
*
* Write must be protected with @lock_write and @unlock_write. While the flash
* is locked, @read returns -EBUSY.
*/
struct intel_m10bmc_flash_bulk_ops {
int (*read)(struct intel_m10bmc *m10bmc, u8 *buf, u32 addr, u32 size);
int (*write)(struct intel_m10bmc *m10bmc, const u8 *buf, u32 offset, u32 size);
int (*lock_write)(struct intel_m10bmc *m10bmc);
void (*unlock_write)(struct intel_m10bmc *m10bmc);
};
/**
* struct intel_m10bmc - Intel MAX 10 BMC parent driver data structure
* @dev: this device
* @regmap: the regmap used to access registers by m10bmc itself
* @info: the platform information for MAX10 BMC
* @flash_bulk_ops: optional device specific operations for flash R/W
*/
struct intel_m10bmc {
struct device *dev;
struct regmap *regmap;
const struct intel_m10bmc_platform_info *info;
const struct intel_m10bmc_flash_bulk_ops *flash_bulk_ops;
};
/*
......@@ -152,11 +270,22 @@ m10bmc_raw_read(struct intel_m10bmc *m10bmc, unsigned int addr,
* The base of the system registers could be configured by HW developers, and
* in HW SPEC, the base is not added to the addresses of the system registers.
*
* This macro helps to simplify the accessing of the system registers. And if
* This function helps to simplify the accessing of the system registers. And if
* the base is reconfigured in HW, SW developers could simply change the
* M10BMC_SYS_BASE accordingly.
* csr_map's base accordingly.
*/
static inline int m10bmc_sys_read(struct intel_m10bmc *m10bmc, unsigned int offset,
unsigned int *val)
{
const struct m10bmc_csr_map *csr_map = m10bmc->info->csr_map;
return m10bmc_raw_read(m10bmc, csr_map->base + offset, val);
}
/*
* MAX10 BMC Core support
*/
#define m10bmc_sys_read(m10bmc, offset, val) \
m10bmc_raw_read(m10bmc, M10BMC_SYS_BASE + (offset), val)
int m10bmc_dev_init(struct intel_m10bmc *m10bmc, const struct intel_m10bmc_platform_info *info);
extern const struct attribute_group *m10bmc_dev_groups[];
#endif /* __MFD_INTEL_M10_BMC_H */
......@@ -18,6 +18,7 @@ enum intel_cht_wc_models {
INTEL_CHT_WC_GPD_WIN_POCKET,
INTEL_CHT_WC_XIAOMI_MIPAD2,
INTEL_CHT_WC_LENOVO_YOGABOOK1,
INTEL_CHT_WC_LENOVO_YT3_X90,
};
/**
......
......@@ -34,5 +34,5 @@ static inline u16 ntxec_reg8(u8 value)
/* Known firmware versions */
#define NTXEC_VERSION_KOBO_AURA 0xd726 /* found in Kobo Aura */
#define NTXEC_VERSION_TOLINO_SHINE2 0xf110 /* found in Tolino Shine 2 HD */
#define NTXEC_VERSION_TOLINO_VISION 0xe135 /* found in Tolino Vision, contains RTC, ADC, PWM, home pad */
#endif
......@@ -69,6 +69,8 @@ enum twl6030_module_ids {
TWL6030_MODULE_GPADC,
TWL6030_MODULE_GASGAUGE,
/* A few extra registers before the registers shared with the 6030 */
TWL6032_MODULE_CHARGE,
TWL6030_MODULE_LAST,
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment