From 5a0b8cb46624cc17fda676d6ae44fb72504f0ad9 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 13 Mar 2018 14:23:28 -0700 Subject: iio: cros_ec: Move cros_ec_sensors_core.h in /include Similar to other common iio frameworks, move cros_ec_sensors_core.h from drivers/iio/common/cros_ec_sensors/ to include/linux/iio/common. Signed-off-by: Gwendal Grignou Signed-off-by: Jonathan Cameron --- include/linux/iio/common/cros_ec_sensors_core.h | 180 ++++++++++++++++++++++++ 1 file changed, 180 insertions(+) create mode 100644 include/linux/iio/common/cros_ec_sensors_core.h (limited to 'include/linux') diff --git a/include/linux/iio/common/cros_ec_sensors_core.h b/include/linux/iio/common/cros_ec_sensors_core.h new file mode 100644 index 000000000000..ce16445411ac --- /dev/null +++ b/include/linux/iio/common/cros_ec_sensors_core.h @@ -0,0 +1,180 @@ +/* + * ChromeOS EC sensor hub + * + * Copyright (C) 2016 Google, Inc + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __CROS_EC_SENSORS_CORE_H +#define __CROS_EC_SENSORS_CORE_H + +#include +#include +#include + +enum { + CROS_EC_SENSOR_X, + CROS_EC_SENSOR_Y, + CROS_EC_SENSOR_Z, + CROS_EC_SENSOR_MAX_AXIS, +}; + +/* EC returns sensor values using signed 16 bit registers */ +#define CROS_EC_SENSOR_BITS 16 + +/* + * 4 16 bit channels are allowed. + * Good enough for current sensors, they use up to 3 16 bit vectors. + */ +#define CROS_EC_SAMPLE_SIZE (sizeof(s64) * 2) + +/* Minimum sampling period to use when device is suspending */ +#define CROS_EC_MIN_SUSPEND_SAMPLING_FREQUENCY 1000 /* 1 second */ + +/** + * struct cros_ec_sensors_core_state - state data for EC sensors IIO driver + * @ec: cros EC device structure + * @cmd_lock: lock used to prevent simultaneous access to the + * commands. + * @msg: cros EC command structure + * @param: motion sensor parameters structure + * @resp: motion sensor response structure + * @type: type of motion sensor + * @loc: location where the motion sensor is placed + * @calib: calibration parameters. Note that trigger + * captured data will always provide the calibrated + * data + * @samples: static array to hold data from a single capture. + * For each channel we need 2 bytes, except for + * the timestamp. The timestamp is always last and + * is always 8-byte aligned. + * @read_ec_sensors_data: function used for accessing sensors values + * @cuur_sampl_freq: current sampling period + */ +struct cros_ec_sensors_core_state { + struct cros_ec_device *ec; + struct mutex cmd_lock; + + struct cros_ec_command *msg; + struct ec_params_motion_sense param; + struct ec_response_motion_sense *resp; + + enum motionsensor_type type; + enum motionsensor_location loc; + + s16 calib[CROS_EC_SENSOR_MAX_AXIS]; + + u8 samples[CROS_EC_SAMPLE_SIZE]; + + int (*read_ec_sensors_data)(struct iio_dev *indio_dev, + unsigned long scan_mask, s16 *data); + + int curr_sampl_freq; +}; + +/** + * cros_ec_sensors_read_lpc() - retrieve data from EC shared memory + * @indio_dev: pointer to IIO device + * @scan_mask: bitmap of the sensor indices to scan + * @data: location to store data + * + * This is the safe function for reading the EC data. It guarantees that the + * data sampled was not modified by the EC while being read. + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev, unsigned long scan_mask, + s16 *data); + +/** + * cros_ec_sensors_read_cmd() - retrieve data using the EC command protocol + * @indio_dev: pointer to IIO device + * @scan_mask: bitmap of the sensor indices to scan + * @data: location to store data + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_sensors_read_cmd(struct iio_dev *indio_dev, unsigned long scan_mask, + s16 *data); + +struct platform_device; +/** + * cros_ec_sensors_core_init() - basic initialization of the core structure + * @pdev: platform device created for the sensors + * @indio_dev: iio device structure of the device + * @physical_device: true if the device refers to a physical device + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_sensors_core_init(struct platform_device *pdev, + struct iio_dev *indio_dev, bool physical_device); + +/** + * cros_ec_sensors_capture() - the trigger handler function + * @irq: the interrupt number. + * @p: a pointer to the poll function. + * + * On a trigger event occurring, if the pollfunc is attached then this + * handler is called as a threaded interrupt (and hence may sleep). It + * is responsible for grabbing data from the device and pushing it into + * the associated buffer. + * + * Return: IRQ_HANDLED + */ +irqreturn_t cros_ec_sensors_capture(int irq, void *p); + +/** + * cros_ec_motion_send_host_cmd() - send motion sense host command + * @st: pointer to state information for device + * @opt_length: optional length to reduce the response size, useful on the data + * path. Otherwise, the maximal allowed response size is used + * + * When called, the sub-command is assumed to be set in param->cmd. + * + * Return: 0 on success, -errno on failure. + */ +int cros_ec_motion_send_host_cmd(struct cros_ec_sensors_core_state *st, + u16 opt_length); + +/** + * cros_ec_sensors_core_read() - function to request a value from the sensor + * @st: pointer to state information for device + * @chan: channel specification structure table + * @val: will contain one element making up the returned value + * @val2: will contain another element making up the returned value + * @mask: specifies which values to be requested + * + * Return: the type of value returned by the device + */ +int cros_ec_sensors_core_read(struct cros_ec_sensors_core_state *st, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask); + +/** + * cros_ec_sensors_core_write() - function to write a value to the sensor + * @st: pointer to state information for device + * @chan: channel specification structure table + * @val: first part of value to write + * @val2: second part of value to write + * @mask: specifies which values to write + * + * Return: the type of value returned by the device + */ +int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st, + struct iio_chan_spec const *chan, + int val, int val2, long mask); + +extern const struct dev_pm_ops cros_ec_sensors_pm_ops; + +/* List of extended channel specification for all sensors */ +extern const struct iio_chan_spec_ext_info cros_ec_sensors_ext_info[]; + +#endif /* __CROS_EC_SENSORS_CORE_H */ -- cgit From 192af06a287bbfcae0de8fa456dca15767ecd056 Mon Sep 17 00:00:00 2001 From: Alexandru Ardelean Date: Mon, 12 Mar 2018 17:48:15 +0200 Subject: iio: adc: ad7780: remove IIO_CHAN_INFO_SAMP_FREQ support The `ad7780` driver does not implement setting/getting the sampling frequency. For the ad7780/ad7781 devices, the control is done via an external pin, and the ad7170/ad7171 devices have a fixed sampling rate (so, no control). For these devices, and similar other that may be added later on, a AD_SD_CHANNEL_NO_SAMPLE_FREQ() macro has been added, which doesn't set the IIO_CHAN_INFO_SAMP_FREQ flag. Signed-off-by: Alexandru Ardelean Signed-off-by: Jonathan Cameron --- drivers/staging/iio/adc/ad7780.c | 2 +- include/linux/iio/adc/ad_sigma_delta.h | 24 +++++++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/staging/iio/adc/ad7780.c b/drivers/staging/iio/adc/ad7780.c index a7797af579b9..16d72072c076 100644 --- a/drivers/staging/iio/adc/ad7780.c +++ b/drivers/staging/iio/adc/ad7780.c @@ -128,7 +128,7 @@ static const struct ad_sigma_delta_info ad7780_sigma_delta_info = { }; #define AD7780_CHANNEL(bits, wordsize) \ - AD_SD_CHANNEL(1, 0, 0, bits, 32, wordsize - bits) + AD_SD_CHANNEL_NO_SAMP_FREQ(1, 0, 0, bits, 32, wordsize - bits) static const struct ad7780_chip_info ad7780_chip_info_tbl[] = { [ID_AD7170] = { diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 1fc7abd28b0b..730ead1a46df 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -127,7 +127,7 @@ void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev); int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig); #define __AD_SD_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ - _storagebits, _shift, _extend_name, _type) \ + _storagebits, _shift, _extend_name, _type, _mask_all) \ { \ .type = (_type), \ .differential = (_channel2 == -1 ? 0 : 1), \ @@ -139,7 +139,7 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig); .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_OFFSET), \ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ - .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_all = _mask_all, \ .scan_index = (_si), \ .scan_type = { \ .sign = 'u', \ @@ -153,25 +153,35 @@ int ad_sd_validate_trigger(struct iio_dev *indio_dev, struct iio_trigger *trig); #define AD_SD_DIFF_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ _storagebits, _shift) \ __AD_SD_CHANNEL(_si, _channel1, _channel2, _address, _bits, \ - _storagebits, _shift, NULL, IIO_VOLTAGE) + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) #define AD_SD_SHORTED_CHANNEL(_si, _channel, _address, _bits, \ _storagebits, _shift) \ __AD_SD_CHANNEL(_si, _channel, _channel, _address, _bits, \ - _storagebits, _shift, "shorted", IIO_VOLTAGE) + _storagebits, _shift, "shorted", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) #define AD_SD_CHANNEL(_si, _channel, _address, _bits, \ _storagebits, _shift) \ __AD_SD_CHANNEL(_si, _channel, -1, _address, _bits, \ - _storagebits, _shift, NULL, IIO_VOLTAGE) + _storagebits, _shift, NULL, IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) + +#define AD_SD_CHANNEL_NO_SAMP_FREQ(_si, _channel, _address, _bits, \ + _storagebits, _shift) \ + __AD_SD_CHANNEL(_si, _channel, -1, _address, _bits, \ + _storagebits, _shift, NULL, IIO_VOLTAGE, 0) #define AD_SD_TEMP_CHANNEL(_si, _address, _bits, _storagebits, _shift) \ __AD_SD_CHANNEL(_si, 0, -1, _address, _bits, \ - _storagebits, _shift, NULL, IIO_TEMP) + _storagebits, _shift, NULL, IIO_TEMP, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) #define AD_SD_SUPPLY_CHANNEL(_si, _channel, _address, _bits, _storagebits, \ _shift) \ __AD_SD_CHANNEL(_si, _channel, -1, _address, _bits, \ - _storagebits, _shift, "supply", IIO_VOLTAGE) + _storagebits, _shift, "supply", IIO_VOLTAGE, \ + BIT(IIO_CHAN_INFO_SAMP_FREQ)) #endif -- cgit From ec1ba3e519c0f46523cf40b83dc71562171b7c08 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 Mar 2018 11:17:40 +0100 Subject: regulator: ab8500: Drop AB8540/9540 support The AB8540 was an evolved version of the AB8500, but it was never mass produced or put into products, only reference designs exist. The upstream support was never completed and it is unlikely that this will happen so drop the support for now to simplify maintenance of the AB8500. Cc: Loic Pallardy Signed-off-by: Linus Walleij Signed-off-by: Mark Brown --- drivers/regulator/ab8500.c | 1779 ++++---------------------------------- include/linux/regulator/ab8500.h | 157 +--- 2 files changed, 188 insertions(+), 1748 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/ab8500.c b/drivers/regulator/ab8500.c index 0f97514e3474..83dba3fbfe0c 100644 --- a/drivers/regulator/ab8500.c +++ b/drivers/regulator/ab8500.c @@ -132,33 +132,6 @@ static const unsigned int ldo_vaux56_voltages[] = { 2790000, }; -static const unsigned int ldo_vaux3_ab8540_voltages[] = { - 1200000, - 1500000, - 1800000, - 2100000, - 2500000, - 2750000, - 2790000, - 2910000, - 3050000, -}; - -static const unsigned int ldo_vaux56_ab8540_voltages[] = { - 750000, 760000, 770000, 780000, 790000, 800000, - 810000, 820000, 830000, 840000, 850000, 860000, - 870000, 880000, 890000, 900000, 910000, 920000, - 930000, 940000, 950000, 960000, 970000, 980000, - 990000, 1000000, 1010000, 1020000, 1030000, - 1040000, 1050000, 1060000, 1070000, 1080000, - 1090000, 1100000, 1110000, 1120000, 1130000, - 1140000, 1150000, 1160000, 1170000, 1180000, - 1190000, 1200000, 1210000, 1220000, 1230000, - 1240000, 1250000, 1260000, 1270000, 1280000, - 1290000, 1300000, 1310000, 1320000, 1330000, - 1340000, 1350000, 1360000, 1800000, 2790000, -}; - static const unsigned int ldo_vintcore_voltages[] = { 1200000, 1225000, @@ -232,8 +205,6 @@ static const unsigned int ldo_vdmic_voltages[] = { static DEFINE_MUTEX(shared_mode_mutex); static struct ab8500_shared_mode ldo_anamic1_shared; static struct ab8500_shared_mode ldo_anamic2_shared; -static struct ab8500_shared_mode ab8540_ldo_anamic1_shared; -static struct ab8500_shared_mode ab8540_ldo_anamic2_shared; static int ab8500_regulator_enable(struct regulator_dev *rdev) { @@ -507,53 +478,6 @@ static int ab8500_regulator_get_voltage_sel(struct regulator_dev *rdev) return (regval & info->voltage_mask) >> voltage_shift; } -static int ab8540_aux3_regulator_get_voltage_sel(struct regulator_dev *rdev) -{ - int ret, voltage_shift; - struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); - u8 regval, regval_expand; - - if (info == NULL) { - dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); - return -EINVAL; - } - - ret = abx500_get_register_interruptible(info->dev, - info->expand_register.voltage_bank, - info->expand_register.voltage_reg, ®val_expand); - if (ret < 0) { - dev_err(rdev_get_dev(rdev), - "couldn't read voltage expand reg for regulator\n"); - return ret; - } - - dev_vdbg(rdev_get_dev(rdev), - "%s-get_voltage expand (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", - info->desc.name, info->expand_register.voltage_bank, - info->expand_register.voltage_reg, - info->expand_register.voltage_mask, regval_expand); - - if (regval_expand & info->expand_register.voltage_mask) - return info->expand_register.voltage_limit; - - ret = abx500_get_register_interruptible(info->dev, - info->voltage_bank, info->voltage_reg, ®val); - if (ret < 0) { - dev_err(rdev_get_dev(rdev), - "couldn't read voltage reg for regulator\n"); - return ret; - } - - dev_vdbg(rdev_get_dev(rdev), - "%s-get_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", - info->desc.name, info->voltage_bank, info->voltage_reg, - info->voltage_mask, regval); - - voltage_shift = ffs(info->voltage_mask) - 1; - - return (regval & info->voltage_mask) >> voltage_shift; -} - static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev, unsigned selector) { @@ -586,61 +510,6 @@ static int ab8500_regulator_set_voltage_sel(struct regulator_dev *rdev, return ret; } -static int ab8540_aux3_regulator_set_voltage_sel(struct regulator_dev *rdev, - unsigned selector) -{ - int ret; - struct ab8500_regulator_info *info = rdev_get_drvdata(rdev); - u8 regval, regval_expand; - - if (info == NULL) { - dev_err(rdev_get_dev(rdev), "regulator info null pointer\n"); - return -EINVAL; - } - - if (selector < info->expand_register.voltage_limit) { - int voltage_shift = ffs(info->voltage_mask) - 1; - - regval = (u8)selector << voltage_shift; - ret = abx500_mask_and_set_register_interruptible(info->dev, - info->voltage_bank, info->voltage_reg, - info->voltage_mask, regval); - if (ret < 0) { - dev_err(rdev_get_dev(rdev), - "couldn't set voltage reg for regulator\n"); - return ret; - } - - dev_vdbg(rdev_get_dev(rdev), - "%s-set_voltage (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", - info->desc.name, info->voltage_bank, info->voltage_reg, - info->voltage_mask, regval); - - regval_expand = 0; - } else { - regval_expand = info->expand_register.voltage_mask; - } - - ret = abx500_mask_and_set_register_interruptible(info->dev, - info->expand_register.voltage_bank, - info->expand_register.voltage_reg, - info->expand_register.voltage_mask, - regval_expand); - if (ret < 0) { - dev_err(rdev_get_dev(rdev), - "couldn't set expand voltage reg for regulator\n"); - return ret; - } - - dev_vdbg(rdev_get_dev(rdev), - "%s-set_voltage expand (bank, reg, mask, value): 0x%x, 0x%x, 0x%x, 0x%x\n", - info->desc.name, info->expand_register.voltage_bank, - info->expand_register.voltage_reg, - info->expand_register.voltage_mask, regval_expand); - - return 0; -} - static struct regulator_ops ab8500_regulator_volt_mode_ops = { .enable = ab8500_regulator_enable, .disable = ab8500_regulator_disable, @@ -653,18 +522,6 @@ static struct regulator_ops ab8500_regulator_volt_mode_ops = { .list_voltage = regulator_list_voltage_table, }; -static struct regulator_ops ab8540_aux3_regulator_volt_mode_ops = { - .enable = ab8500_regulator_enable, - .disable = ab8500_regulator_disable, - .get_optimum_mode = ab8500_regulator_get_optimum_mode, - .set_mode = ab8500_regulator_set_mode, - .get_mode = ab8500_regulator_get_mode, - .is_enabled = ab8500_regulator_is_enabled, - .get_voltage_sel = ab8540_aux3_regulator_get_voltage_sel, - .set_voltage_sel = ab8540_aux3_regulator_set_voltage_sel, - .list_voltage = regulator_list_voltage_table, -}; - static struct regulator_ops ab8500_regulator_volt_ops = { .enable = ab8500_regulator_enable, .disable = ab8500_regulator_disable, @@ -1217,1156 +1074,118 @@ static struct ab8500_regulator_info }, }; -/* AB9540 regulator information */ -static struct ab8500_regulator_info - ab9540_regulator_info[AB9540_NUM_REGULATORS] = { +static struct ab8500_shared_mode ldo_anamic1_shared = { + .shared_regulator = &ab8505_regulator_info[AB8505_LDO_ANAMIC2], +}; + +static struct ab8500_shared_mode ldo_anamic2_shared = { + .shared_regulator = &ab8505_regulator_info[AB8505_LDO_ANAMIC1], +}; + +struct ab8500_reg_init { + u8 bank; + u8 addr; + u8 mask; +}; + +#define REG_INIT(_id, _bank, _addr, _mask) \ + [_id] = { \ + .bank = _bank, \ + .addr = _addr, \ + .mask = _mask, \ + } + +/* AB8500 register init */ +static struct ab8500_reg_init ab8500_reg_init[] = { /* - * Variable Voltage Regulators - * name, min mV, max mV, - * update bank, reg, mask, enable val - * volt bank, reg, mask + * 0x30, VanaRequestCtrl + * 0xc0, VextSupply1RequestCtrl */ - [AB9540_LDO_AUX1] = { - .desc = { - .name = "LDO-AUX1", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_AUX1, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), - .volt_table = ldo_vauxn_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x04, - .update_reg = 0x09, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - .voltage_bank = 0x04, - .voltage_reg = 0x1f, - .voltage_mask = 0x0f, - }, - [AB9540_LDO_AUX2] = { - .desc = { - .name = "LDO-AUX2", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_AUX2, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), - .volt_table = ldo_vauxn_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x04, - .update_reg = 0x09, - .update_mask = 0x0c, - .update_val = 0x04, - .update_val_idle = 0x0c, - .update_val_normal = 0x04, - .voltage_bank = 0x04, - .voltage_reg = 0x20, - .voltage_mask = 0x0f, - }, - [AB9540_LDO_AUX3] = { - .desc = { - .name = "LDO-AUX3", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_AUX3, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vaux3_voltages), - .volt_table = ldo_vaux3_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x04, - .update_reg = 0x0a, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - .voltage_bank = 0x04, - .voltage_reg = 0x21, - .voltage_mask = 0x07, - }, - [AB9540_LDO_AUX4] = { - .desc = { - .name = "LDO-AUX4", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_AUX4, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), - .volt_table = ldo_vauxn_voltages, - }, - .load_lp_uA = 5000, - /* values for Vaux4Regu register */ - .update_bank = 0x04, - .update_reg = 0x2e, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - /* values for Vaux4SEL register */ - .voltage_bank = 0x04, - .voltage_reg = 0x2f, - .voltage_mask = 0x0f, - }, - [AB9540_LDO_INTCORE] = { - .desc = { - .name = "LDO-INTCORE", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_INTCORE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), - .volt_table = ldo_vintcore_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x03, - .update_reg = 0x80, - .update_mask = 0x44, - .update_val = 0x44, - .update_val_idle = 0x44, - .update_val_normal = 0x04, - .voltage_bank = 0x03, - .voltage_reg = 0x80, - .voltage_mask = 0x38, - }, - + REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xf0), /* - * Fixed Voltage Regulators - * name, fixed mV, - * update bank, reg, mask, enable val + * 0x03, VextSupply2RequestCtrl + * 0x0c, VextSupply3RequestCtrl + * 0x30, Vaux1RequestCtrl + * 0xc0, Vaux2RequestCtrl */ - [AB9540_LDO_TVOUT] = { - .desc = { - .name = "LDO-TVOUT", - .ops = &ab8500_regulator_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_TVOUT, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2000000_voltage, - .enable_time = 10000, - }, - .load_lp_uA = 1000, - .update_bank = 0x03, - .update_reg = 0x80, - .update_mask = 0x82, - .update_val = 0x02, - .update_val_idle = 0x82, - .update_val_normal = 0x02, - }, - [AB9540_LDO_USB] = { - .desc = { - .name = "LDO-USB", - .ops = &ab8500_regulator_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_USB, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_3300000_voltage, - }, - .update_bank = 0x03, - .update_reg = 0x82, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - }, - [AB9540_LDO_AUDIO] = { - .desc = { - .name = "LDO-AUDIO", - .ops = &ab8500_regulator_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_AUDIO, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2000000_voltage, - }, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x02, - .update_val = 0x02, - }, - [AB9540_LDO_ANAMIC1] = { - .desc = { - .name = "LDO-ANAMIC1", - .ops = &ab8500_regulator_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_ANAMIC1, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2050000_voltage, - }, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x08, - .update_val = 0x08, - }, - [AB9540_LDO_ANAMIC2] = { - .desc = { - .name = "LDO-ANAMIC2", - .ops = &ab8500_regulator_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_ANAMIC2, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2050000_voltage, - }, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x10, - .update_val = 0x10, - }, - [AB9540_LDO_DMIC] = { - .desc = { - .name = "LDO-DMIC", - .ops = &ab8500_regulator_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_DMIC, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_1800000_voltage, - }, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x04, - .update_val = 0x04, - }, - - /* - * Regulators with fixed voltage and normal/idle modes - */ - [AB9540_LDO_ANA] = { - .desc = { - .name = "LDO-ANA", - .ops = &ab8500_regulator_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB9540_LDO_ANA, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_1200000_voltage, - }, - .load_lp_uA = 1000, - .update_bank = 0x04, - .update_reg = 0x06, - .update_mask = 0x0c, - .update_val = 0x08, - .update_val_idle = 0x0c, - .update_val_normal = 0x08, - }, -}; - -/* AB8540 regulator information */ -static struct ab8500_regulator_info - ab8540_regulator_info[AB8540_NUM_REGULATORS] = { - /* - * Variable Voltage Regulators - * name, min mV, max mV, - * update bank, reg, mask, enable val - * volt bank, reg, mask - */ - [AB8540_LDO_AUX1] = { - .desc = { - .name = "LDO-AUX1", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_AUX1, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), - .volt_table = ldo_vauxn_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x04, - .update_reg = 0x09, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - .voltage_bank = 0x04, - .voltage_reg = 0x1f, - .voltage_mask = 0x0f, - }, - [AB8540_LDO_AUX2] = { - .desc = { - .name = "LDO-AUX2", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_AUX2, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), - .volt_table = ldo_vauxn_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x04, - .update_reg = 0x09, - .update_mask = 0x0c, - .update_val = 0x04, - .update_val_idle = 0x0c, - .update_val_normal = 0x04, - .voltage_bank = 0x04, - .voltage_reg = 0x20, - .voltage_mask = 0x0f, - }, - [AB8540_LDO_AUX3] = { - .desc = { - .name = "LDO-AUX3", - .ops = &ab8540_aux3_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_AUX3, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vaux3_ab8540_voltages), - .volt_table = ldo_vaux3_ab8540_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x04, - .update_reg = 0x0a, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - .voltage_bank = 0x04, - .voltage_reg = 0x21, - .voltage_mask = 0x07, - .expand_register = { - .voltage_limit = 8, - .voltage_bank = 0x04, - .voltage_reg = 0x01, - .voltage_mask = 0x10, - } - }, - [AB8540_LDO_AUX4] = { - .desc = { - .name = "LDO-AUX4", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_AUX4, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vauxn_voltages), - .volt_table = ldo_vauxn_voltages, - }, - .load_lp_uA = 5000, - /* values for Vaux4Regu register */ - .update_bank = 0x04, - .update_reg = 0x2e, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - /* values for Vaux4SEL register */ - .voltage_bank = 0x04, - .voltage_reg = 0x2f, - .voltage_mask = 0x0f, - }, - [AB8540_LDO_AUX5] = { - .desc = { - .name = "LDO-AUX5", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_AUX5, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vaux56_ab8540_voltages), - .volt_table = ldo_vaux56_ab8540_voltages, - }, - .load_lp_uA = 20000, - /* values for Vaux5Regu register */ - .update_bank = 0x04, - .update_reg = 0x32, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - /* values for Vaux5SEL register */ - .voltage_bank = 0x04, - .voltage_reg = 0x33, - .voltage_mask = 0x3f, - }, - [AB8540_LDO_AUX6] = { - .desc = { - .name = "LDO-AUX6", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_AUX6, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vaux56_ab8540_voltages), - .volt_table = ldo_vaux56_ab8540_voltages, - }, - .load_lp_uA = 20000, - /* values for Vaux6Regu register */ - .update_bank = 0x04, - .update_reg = 0x35, - .update_mask = 0x03, - .update_val = 0x01, - .update_val_idle = 0x03, - .update_val_normal = 0x01, - /* values for Vaux6SEL register */ - .voltage_bank = 0x04, - .voltage_reg = 0x36, - .voltage_mask = 0x3f, - }, - [AB8540_LDO_INTCORE] = { - .desc = { - .name = "LDO-INTCORE", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_INTCORE, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vintcore_voltages), - .volt_table = ldo_vintcore_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x03, - .update_reg = 0x80, - .update_mask = 0x44, - .update_val = 0x44, - .update_val_idle = 0x44, - .update_val_normal = 0x04, - .voltage_bank = 0x03, - .voltage_reg = 0x80, - .voltage_mask = 0x38, - }, - - /* - * Fixed Voltage Regulators - * name, fixed mV, - * update bank, reg, mask, enable val - */ - [AB8540_LDO_TVOUT] = { - .desc = { - .name = "LDO-TVOUT", - .ops = &ab8500_regulator_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_TVOUT, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2000000_voltage, - .enable_time = 10000, - }, - .load_lp_uA = 1000, - .update_bank = 0x03, - .update_reg = 0x80, - .update_mask = 0x82, - .update_val = 0x02, - .update_val_idle = 0x82, - .update_val_normal = 0x02, - }, - [AB8540_LDO_AUDIO] = { - .desc = { - .name = "LDO-AUDIO", - .ops = &ab8500_regulator_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_AUDIO, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2000000_voltage, - }, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x02, - .update_val = 0x02, - }, - [AB8540_LDO_ANAMIC1] = { - .desc = { - .name = "LDO-ANAMIC1", - .ops = &ab8500_regulator_anamic_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_ANAMIC1, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2050000_voltage, - }, - .shared_mode = &ab8540_ldo_anamic1_shared, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x08, - .update_val = 0x08, - .mode_bank = 0x03, - .mode_reg = 0x83, - .mode_mask = 0x20, - .mode_val_idle = 0x20, - .mode_val_normal = 0x00, - }, - [AB8540_LDO_ANAMIC2] = { - .desc = { - .name = "LDO-ANAMIC2", - .ops = &ab8500_regulator_anamic_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_ANAMIC2, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_2050000_voltage, - }, - .shared_mode = &ab8540_ldo_anamic2_shared, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x10, - .update_val = 0x10, - .mode_bank = 0x03, - .mode_reg = 0x83, - .mode_mask = 0x20, - .mode_val_idle = 0x20, - .mode_val_normal = 0x00, - }, - [AB8540_LDO_DMIC] = { - .desc = { - .name = "LDO-DMIC", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_DMIC, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_vdmic_voltages), - .volt_table = ldo_vdmic_voltages, - }, - .load_lp_uA = 1000, - .update_bank = 0x03, - .update_reg = 0x83, - .update_mask = 0x04, - .update_val = 0x04, - .voltage_bank = 0x03, - .voltage_reg = 0x83, - .voltage_mask = 0xc0, - }, - - /* - * Regulators with fixed voltage and normal/idle modes - */ - [AB8540_LDO_ANA] = { - .desc = { - .name = "LDO-ANA", - .ops = &ab8500_regulator_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_ANA, - .owner = THIS_MODULE, - .n_voltages = 1, - .volt_table = fixed_1200000_voltage, - }, - .load_lp_uA = 1000, - .update_bank = 0x04, - .update_reg = 0x06, - .update_mask = 0x0c, - .update_val = 0x04, - .update_val_idle = 0x0c, - .update_val_normal = 0x04, - }, - [AB8540_LDO_SDIO] = { - .desc = { - .name = "LDO-SDIO", - .ops = &ab8500_regulator_volt_mode_ops, - .type = REGULATOR_VOLTAGE, - .id = AB8540_LDO_SDIO, - .owner = THIS_MODULE, - .n_voltages = ARRAY_SIZE(ldo_sdio_voltages), - .volt_table = ldo_sdio_voltages, - }, - .load_lp_uA = 5000, - .update_bank = 0x03, - .update_reg = 0x88, - .update_mask = 0x30, - .update_val = 0x10, - .update_val_idle = 0x30, - .update_val_normal = 0x10, - .voltage_bank = 0x03, - .voltage_reg = 0x88, - .voltage_mask = 0x07, - }, -}; - -static struct ab8500_shared_mode ldo_anamic1_shared = { - .shared_regulator = &ab8505_regulator_info[AB8505_LDO_ANAMIC2], -}; - -static struct ab8500_shared_mode ldo_anamic2_shared = { - .shared_regulator = &ab8505_regulator_info[AB8505_LDO_ANAMIC1], -}; - -static struct ab8500_shared_mode ab8540_ldo_anamic1_shared = { - .shared_regulator = &ab8540_regulator_info[AB8540_LDO_ANAMIC2], -}; - -static struct ab8500_shared_mode ab8540_ldo_anamic2_shared = { - .shared_regulator = &ab8540_regulator_info[AB8540_LDO_ANAMIC1], -}; - -struct ab8500_reg_init { - u8 bank; - u8 addr; - u8 mask; -}; - -#define REG_INIT(_id, _bank, _addr, _mask) \ - [_id] = { \ - .bank = _bank, \ - .addr = _addr, \ - .mask = _mask, \ - } - -/* AB8500 register init */ -static struct ab8500_reg_init ab8500_reg_init[] = { - /* - * 0x30, VanaRequestCtrl - * 0xc0, VextSupply1RequestCtrl - */ - REG_INIT(AB8500_REGUREQUESTCTRL2, 0x03, 0x04, 0xf0), - /* - * 0x03, VextSupply2RequestCtrl - * 0x0c, VextSupply3RequestCtrl - * 0x30, Vaux1RequestCtrl - * 0xc0, Vaux2RequestCtrl - */ - REG_INIT(AB8500_REGUREQUESTCTRL3, 0x03, 0x05, 0xff), - /* - * 0x03, Vaux3RequestCtrl - * 0x04, SwHPReq - */ - REG_INIT(AB8500_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), - /* - * 0x08, VanaSysClkReq1HPValid - * 0x20, Vaux1SysClkReq1HPValid - * 0x40, Vaux2SysClkReq1HPValid - * 0x80, Vaux3SysClkReq1HPValid - */ - REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xe8), - /* - * 0x10, VextSupply1SysClkReq1HPValid - * 0x20, VextSupply2SysClkReq1HPValid - * 0x40, VextSupply3SysClkReq1HPValid - */ - REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x70), - /* - * 0x08, VanaHwHPReq1Valid - * 0x20, Vaux1HwHPReq1Valid - * 0x40, Vaux2HwHPReq1Valid - * 0x80, Vaux3HwHPReq1Valid - */ - REG_INIT(AB8500_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xe8), - /* - * 0x01, VextSupply1HwHPReq1Valid - * 0x02, VextSupply2HwHPReq1Valid - * 0x04, VextSupply3HwHPReq1Valid - */ - REG_INIT(AB8500_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x07), - /* - * 0x08, VanaHwHPReq2Valid - * 0x20, Vaux1HwHPReq2Valid - * 0x40, Vaux2HwHPReq2Valid - * 0x80, Vaux3HwHPReq2Valid - */ - REG_INIT(AB8500_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xe8), - /* - * 0x01, VextSupply1HwHPReq2Valid - * 0x02, VextSupply2HwHPReq2Valid - * 0x04, VextSupply3HwHPReq2Valid - */ - REG_INIT(AB8500_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x07), - /* - * 0x20, VanaSwHPReqValid - * 0x80, Vaux1SwHPReqValid - */ - REG_INIT(AB8500_REGUSWHPREQVALID1, 0x03, 0x0d, 0xa0), - /* - * 0x01, Vaux2SwHPReqValid - * 0x02, Vaux3SwHPReqValid - * 0x04, VextSupply1SwHPReqValid - * 0x08, VextSupply2SwHPReqValid - * 0x10, VextSupply3SwHPReqValid - */ - REG_INIT(AB8500_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f), - /* - * 0x02, SysClkReq2Valid1 - * 0x04, SysClkReq3Valid1 - * 0x08, SysClkReq4Valid1 - * 0x10, SysClkReq5Valid1 - * 0x20, SysClkReq6Valid1 - * 0x40, SysClkReq7Valid1 - * 0x80, SysClkReq8Valid1 - */ - REG_INIT(AB8500_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe), - /* - * 0x02, SysClkReq2Valid2 - * 0x04, SysClkReq3Valid2 - * 0x08, SysClkReq4Valid2 - * 0x10, SysClkReq5Valid2 - * 0x20, SysClkReq6Valid2 - * 0x40, SysClkReq7Valid2 - * 0x80, SysClkReq8Valid2 - */ - REG_INIT(AB8500_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe), - /* - * 0x02, VTVoutEna - * 0x04, Vintcore12Ena - * 0x38, Vintcore12Sel - * 0x40, Vintcore12LP - * 0x80, VTVoutLP - */ - REG_INIT(AB8500_REGUMISC1, 0x03, 0x80, 0xfe), - /* - * 0x02, VaudioEna - * 0x04, VdmicEna - * 0x08, Vamic1Ena - * 0x10, Vamic2Ena - */ - REG_INIT(AB8500_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), - /* - * 0x01, Vamic1_dzout - * 0x02, Vamic2_dzout - */ - REG_INIT(AB8500_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), - /* - * 0x03, VpllRegu (NOTE! PRCMU register bits) - * 0x0c, VanaRegu - */ - REG_INIT(AB8500_VPLLVANAREGU, 0x04, 0x06, 0x0f), - /* - * 0x01, VrefDDREna - * 0x02, VrefDDRSleepMode - */ - REG_INIT(AB8500_VREFDDR, 0x04, 0x07, 0x03), - /* - * 0x03, VextSupply1Regu - * 0x0c, VextSupply2Regu - * 0x30, VextSupply3Regu - * 0x40, ExtSupply2Bypass - * 0x80, ExtSupply3Bypass - */ - REG_INIT(AB8500_EXTSUPPLYREGU, 0x04, 0x08, 0xff), - /* - * 0x03, Vaux1Regu - * 0x0c, Vaux2Regu - */ - REG_INIT(AB8500_VAUX12REGU, 0x04, 0x09, 0x0f), - /* - * 0x03, Vaux3Regu - */ - REG_INIT(AB8500_VRF1VAUX3REGU, 0x04, 0x0a, 0x03), - /* - * 0x0f, Vaux1Sel - */ - REG_INIT(AB8500_VAUX1SEL, 0x04, 0x1f, 0x0f), - /* - * 0x0f, Vaux2Sel - */ - REG_INIT(AB8500_VAUX2SEL, 0x04, 0x20, 0x0f), - /* - * 0x07, Vaux3Sel - */ - REG_INIT(AB8500_VRF1VAUX3SEL, 0x04, 0x21, 0x07), - /* - * 0x01, VextSupply12LP - */ - REG_INIT(AB8500_REGUCTRL2SPARE, 0x04, 0x22, 0x01), - /* - * 0x04, Vaux1Disch - * 0x08, Vaux2Disch - * 0x10, Vaux3Disch - * 0x20, Vintcore12Disch - * 0x40, VTVoutDisch - * 0x80, VaudioDisch - */ - REG_INIT(AB8500_REGUCTRLDISCH, 0x04, 0x43, 0xfc), - /* - * 0x02, VanaDisch - * 0x04, VdmicPullDownEna - * 0x10, VdmicDisch - */ - REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16), -}; - -/* AB8505 register init */ -static struct ab8500_reg_init ab8505_reg_init[] = { - /* - * 0x03, VarmRequestCtrl - * 0x0c, VsmpsCRequestCtrl - * 0x30, VsmpsARequestCtrl - * 0xc0, VsmpsBRequestCtrl - */ - REG_INIT(AB8505_REGUREQUESTCTRL1, 0x03, 0x03, 0xff), - /* - * 0x03, VsafeRequestCtrl - * 0x0c, VpllRequestCtrl - * 0x30, VanaRequestCtrl - */ - REG_INIT(AB8505_REGUREQUESTCTRL2, 0x03, 0x04, 0x3f), - /* - * 0x30, Vaux1RequestCtrl - * 0xc0, Vaux2RequestCtrl - */ - REG_INIT(AB8505_REGUREQUESTCTRL3, 0x03, 0x05, 0xf0), - /* - * 0x03, Vaux3RequestCtrl - * 0x04, SwHPReq - */ - REG_INIT(AB8505_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), - /* - * 0x01, VsmpsASysClkReq1HPValid - * 0x02, VsmpsBSysClkReq1HPValid - * 0x04, VsafeSysClkReq1HPValid - * 0x08, VanaSysClkReq1HPValid - * 0x10, VpllSysClkReq1HPValid - * 0x20, Vaux1SysClkReq1HPValid - * 0x40, Vaux2SysClkReq1HPValid - * 0x80, Vaux3SysClkReq1HPValid - */ - REG_INIT(AB8505_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff), - /* - * 0x01, VsmpsCSysClkReq1HPValid - * 0x02, VarmSysClkReq1HPValid - * 0x04, VbbSysClkReq1HPValid - * 0x08, VsmpsMSysClkReq1HPValid - */ - REG_INIT(AB8505_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x0f), - /* - * 0x01, VsmpsAHwHPReq1Valid - * 0x02, VsmpsBHwHPReq1Valid - * 0x04, VsafeHwHPReq1Valid - * 0x08, VanaHwHPReq1Valid - * 0x10, VpllHwHPReq1Valid - * 0x20, Vaux1HwHPReq1Valid - * 0x40, Vaux2HwHPReq1Valid - * 0x80, Vaux3HwHPReq1Valid - */ - REG_INIT(AB8505_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff), - /* - * 0x08, VsmpsMHwHPReq1Valid - */ - REG_INIT(AB8505_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x08), - /* - * 0x01, VsmpsAHwHPReq2Valid - * 0x02, VsmpsBHwHPReq2Valid - * 0x04, VsafeHwHPReq2Valid - * 0x08, VanaHwHPReq2Valid - * 0x10, VpllHwHPReq2Valid - * 0x20, Vaux1HwHPReq2Valid - * 0x40, Vaux2HwHPReq2Valid - * 0x80, Vaux3HwHPReq2Valid - */ - REG_INIT(AB8505_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff), - /* - * 0x08, VsmpsMHwHPReq2Valid - */ - REG_INIT(AB8505_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x08), - /* - * 0x01, VsmpsCSwHPReqValid - * 0x02, VarmSwHPReqValid - * 0x04, VsmpsASwHPReqValid - * 0x08, VsmpsBSwHPReqValid - * 0x10, VsafeSwHPReqValid - * 0x20, VanaSwHPReqValid - * 0x40, VpllSwHPReqValid - * 0x80, Vaux1SwHPReqValid - */ - REG_INIT(AB8505_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff), - /* - * 0x01, Vaux2SwHPReqValid - * 0x02, Vaux3SwHPReqValid - * 0x20, VsmpsMSwHPReqValid - */ - REG_INIT(AB8505_REGUSWHPREQVALID2, 0x03, 0x0e, 0x23), - /* - * 0x02, SysClkReq2Valid1 - * 0x04, SysClkReq3Valid1 - * 0x08, SysClkReq4Valid1 - */ - REG_INIT(AB8505_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0x0e), - /* - * 0x02, SysClkReq2Valid2 - * 0x04, SysClkReq3Valid2 - * 0x08, SysClkReq4Valid2 - */ - REG_INIT(AB8505_REGUSYSCLKREQVALID2, 0x03, 0x10, 0x0e), - /* - * 0x01, Vaux4SwHPReqValid - * 0x02, Vaux4HwHPReq2Valid - * 0x04, Vaux4HwHPReq1Valid - * 0x08, Vaux4SysClkReq1HPValid - */ - REG_INIT(AB8505_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f), - /* - * 0x02, VadcEna - * 0x04, VintCore12Ena - * 0x38, VintCore12Sel - * 0x40, VintCore12LP - * 0x80, VadcLP - */ - REG_INIT(AB8505_REGUMISC1, 0x03, 0x80, 0xfe), - /* - * 0x02, VaudioEna - * 0x04, VdmicEna - * 0x08, Vamic1Ena - * 0x10, Vamic2Ena - */ - REG_INIT(AB8505_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), - /* - * 0x01, Vamic1_dzout - * 0x02, Vamic2_dzout - */ - REG_INIT(AB8505_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), - /* - * 0x03, VsmpsARegu - * 0x0c, VsmpsASelCtrl - * 0x10, VsmpsAAutoMode - * 0x20, VsmpsAPWMMode - */ - REG_INIT(AB8505_VSMPSAREGU, 0x04, 0x03, 0x3f), - /* - * 0x03, VsmpsBRegu - * 0x0c, VsmpsBSelCtrl - * 0x10, VsmpsBAutoMode - * 0x20, VsmpsBPWMMode - */ - REG_INIT(AB8505_VSMPSBREGU, 0x04, 0x04, 0x3f), - /* - * 0x03, VsafeRegu - * 0x0c, VsafeSelCtrl - * 0x10, VsafeAutoMode - * 0x20, VsafePWMMode - */ - REG_INIT(AB8505_VSAFEREGU, 0x04, 0x05, 0x3f), - /* - * 0x03, VpllRegu (NOTE! PRCMU register bits) - * 0x0c, VanaRegu - */ - REG_INIT(AB8505_VPLLVANAREGU, 0x04, 0x06, 0x0f), - /* - * 0x03, VextSupply1Regu - * 0x0c, VextSupply2Regu - * 0x30, VextSupply3Regu - * 0x40, ExtSupply2Bypass - * 0x80, ExtSupply3Bypass - */ - REG_INIT(AB8505_EXTSUPPLYREGU, 0x04, 0x08, 0xff), - /* - * 0x03, Vaux1Regu - * 0x0c, Vaux2Regu - */ - REG_INIT(AB8505_VAUX12REGU, 0x04, 0x09, 0x0f), - /* - * 0x0f, Vaux3Regu - */ - REG_INIT(AB8505_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f), - /* - * 0x3f, VsmpsASel1 - */ - REG_INIT(AB8505_VSMPSASEL1, 0x04, 0x13, 0x3f), - /* - * 0x3f, VsmpsASel2 - */ - REG_INIT(AB8505_VSMPSASEL2, 0x04, 0x14, 0x3f), - /* - * 0x3f, VsmpsASel3 - */ - REG_INIT(AB8505_VSMPSASEL3, 0x04, 0x15, 0x3f), - /* - * 0x3f, VsmpsBSel1 - */ - REG_INIT(AB8505_VSMPSBSEL1, 0x04, 0x17, 0x3f), - /* - * 0x3f, VsmpsBSel2 - */ - REG_INIT(AB8505_VSMPSBSEL2, 0x04, 0x18, 0x3f), - /* - * 0x3f, VsmpsBSel3 - */ - REG_INIT(AB8505_VSMPSBSEL3, 0x04, 0x19, 0x3f), - /* - * 0x7f, VsafeSel1 - */ - REG_INIT(AB8505_VSAFESEL1, 0x04, 0x1b, 0x7f), - /* - * 0x3f, VsafeSel2 - */ - REG_INIT(AB8505_VSAFESEL2, 0x04, 0x1c, 0x7f), - /* - * 0x3f, VsafeSel3 - */ - REG_INIT(AB8505_VSAFESEL3, 0x04, 0x1d, 0x7f), - /* - * 0x0f, Vaux1Sel - */ - REG_INIT(AB8505_VAUX1SEL, 0x04, 0x1f, 0x0f), - /* - * 0x0f, Vaux2Sel - */ - REG_INIT(AB8505_VAUX2SEL, 0x04, 0x20, 0x0f), - /* - * 0x07, Vaux3Sel - * 0x30, VRF1Sel - */ - REG_INIT(AB8505_VRF1VAUX3SEL, 0x04, 0x21, 0x37), - /* - * 0x03, Vaux4RequestCtrl - */ - REG_INIT(AB8505_VAUX4REQCTRL, 0x04, 0x2d, 0x03), - /* - * 0x03, Vaux4Regu - */ - REG_INIT(AB8505_VAUX4REGU, 0x04, 0x2e, 0x03), - /* - * 0x0f, Vaux4Sel - */ - REG_INIT(AB8505_VAUX4SEL, 0x04, 0x2f, 0x0f), - /* - * 0x04, Vaux1Disch - * 0x08, Vaux2Disch - * 0x10, Vaux3Disch - * 0x20, Vintcore12Disch - * 0x40, VTVoutDisch - * 0x80, VaudioDisch - */ - REG_INIT(AB8505_REGUCTRLDISCH, 0x04, 0x43, 0xfc), - /* - * 0x02, VanaDisch - * 0x04, VdmicPullDownEna - * 0x10, VdmicDisch - */ - REG_INIT(AB8505_REGUCTRLDISCH2, 0x04, 0x44, 0x16), - /* - * 0x01, Vaux4Disch - */ - REG_INIT(AB8505_REGUCTRLDISCH3, 0x04, 0x48, 0x01), - /* - * 0x07, Vaux5Sel - * 0x08, Vaux5LP - * 0x10, Vaux5Ena - * 0x20, Vaux5Disch - * 0x40, Vaux5DisSfst - * 0x80, Vaux5DisPulld - */ - REG_INIT(AB8505_CTRLVAUX5, 0x01, 0x55, 0xff), - /* - * 0x07, Vaux6Sel - * 0x08, Vaux6LP - * 0x10, Vaux6Ena - * 0x80, Vaux6DisPulld - */ - REG_INIT(AB8505_CTRLVAUX6, 0x01, 0x56, 0x9f), -}; - -/* AB9540 register init */ -static struct ab8500_reg_init ab9540_reg_init[] = { - /* - * 0x03, VarmRequestCtrl - * 0x0c, VapeRequestCtrl - * 0x30, Vsmps1RequestCtrl - * 0xc0, Vsmps2RequestCtrl - */ - REG_INIT(AB9540_REGUREQUESTCTRL1, 0x03, 0x03, 0xff), - /* - * 0x03, Vsmps3RequestCtrl - * 0x0c, VpllRequestCtrl - * 0x30, VanaRequestCtrl - * 0xc0, VextSupply1RequestCtrl - */ - REG_INIT(AB9540_REGUREQUESTCTRL2, 0x03, 0x04, 0xff), - /* - * 0x03, VextSupply2RequestCtrl - * 0x0c, VextSupply3RequestCtrl - * 0x30, Vaux1RequestCtrl - * 0xc0, Vaux2RequestCtrl - */ - REG_INIT(AB9540_REGUREQUESTCTRL3, 0x03, 0x05, 0xff), + REG_INIT(AB8500_REGUREQUESTCTRL3, 0x03, 0x05, 0xff), /* * 0x03, Vaux3RequestCtrl * 0x04, SwHPReq */ - REG_INIT(AB9540_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), + REG_INIT(AB8500_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), /* - * 0x01, Vsmps1SysClkReq1HPValid - * 0x02, Vsmps2SysClkReq1HPValid - * 0x04, Vsmps3SysClkReq1HPValid * 0x08, VanaSysClkReq1HPValid - * 0x10, VpllSysClkReq1HPValid * 0x20, Vaux1SysClkReq1HPValid * 0x40, Vaux2SysClkReq1HPValid * 0x80, Vaux3SysClkReq1HPValid */ - REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff), + REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xe8), /* - * 0x01, VapeSysClkReq1HPValid - * 0x02, VarmSysClkReq1HPValid - * 0x04, VbbSysClkReq1HPValid - * 0x08, VmodSysClkReq1HPValid * 0x10, VextSupply1SysClkReq1HPValid * 0x20, VextSupply2SysClkReq1HPValid * 0x40, VextSupply3SysClkReq1HPValid */ - REG_INIT(AB9540_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x7f), + REG_INIT(AB8500_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x70), /* - * 0x01, Vsmps1HwHPReq1Valid - * 0x02, Vsmps2HwHPReq1Valid - * 0x04, Vsmps3HwHPReq1Valid * 0x08, VanaHwHPReq1Valid - * 0x10, VpllHwHPReq1Valid * 0x20, Vaux1HwHPReq1Valid * 0x40, Vaux2HwHPReq1Valid * 0x80, Vaux3HwHPReq1Valid */ - REG_INIT(AB9540_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff), + REG_INIT(AB8500_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xe8), /* * 0x01, VextSupply1HwHPReq1Valid * 0x02, VextSupply2HwHPReq1Valid * 0x04, VextSupply3HwHPReq1Valid - * 0x08, VmodHwHPReq1Valid */ - REG_INIT(AB9540_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x0f), + REG_INIT(AB8500_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x07), /* - * 0x01, Vsmps1HwHPReq2Valid - * 0x02, Vsmps2HwHPReq2Valid - * 0x03, Vsmps3HwHPReq2Valid * 0x08, VanaHwHPReq2Valid - * 0x10, VpllHwHPReq2Valid * 0x20, Vaux1HwHPReq2Valid * 0x40, Vaux2HwHPReq2Valid * 0x80, Vaux3HwHPReq2Valid */ - REG_INIT(AB9540_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff), + REG_INIT(AB8500_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xe8), /* * 0x01, VextSupply1HwHPReq2Valid * 0x02, VextSupply2HwHPReq2Valid * 0x04, VextSupply3HwHPReq2Valid - * 0x08, VmodHwHPReq2Valid */ - REG_INIT(AB9540_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x0f), + REG_INIT(AB8500_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x07), /* - * 0x01, VapeSwHPReqValid - * 0x02, VarmSwHPReqValid - * 0x04, Vsmps1SwHPReqValid - * 0x08, Vsmps2SwHPReqValid - * 0x10, Vsmps3SwHPReqValid * 0x20, VanaSwHPReqValid - * 0x40, VpllSwHPReqValid * 0x80, Vaux1SwHPReqValid */ - REG_INIT(AB9540_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff), + REG_INIT(AB8500_REGUSWHPREQVALID1, 0x03, 0x0d, 0xa0), /* * 0x01, Vaux2SwHPReqValid * 0x02, Vaux3SwHPReqValid * 0x04, VextSupply1SwHPReqValid * 0x08, VextSupply2SwHPReqValid * 0x10, VextSupply3SwHPReqValid - * 0x20, VmodSwHPReqValid */ - REG_INIT(AB9540_REGUSWHPREQVALID2, 0x03, 0x0e, 0x3f), + REG_INIT(AB8500_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f), /* * 0x02, SysClkReq2Valid1 - * ... + * 0x04, SysClkReq3Valid1 + * 0x08, SysClkReq4Valid1 + * 0x10, SysClkReq5Valid1 + * 0x20, SysClkReq6Valid1 + * 0x40, SysClkReq7Valid1 * 0x80, SysClkReq8Valid1 */ - REG_INIT(AB9540_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe), + REG_INIT(AB8500_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xfe), /* * 0x02, SysClkReq2Valid2 - * ... + * 0x04, SysClkReq3Valid2 + * 0x08, SysClkReq4Valid2 + * 0x10, SysClkReq5Valid2 + * 0x20, SysClkReq6Valid2 + * 0x40, SysClkReq7Valid2 * 0x80, SysClkReq8Valid2 */ - REG_INIT(AB9540_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe), - /* - * 0x01, Vaux4SwHPReqValid - * 0x02, Vaux4HwHPReq2Valid - * 0x04, Vaux4HwHPReq1Valid - * 0x08, Vaux4SysClkReq1HPValid - */ - REG_INIT(AB9540_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f), + REG_INIT(AB8500_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xfe), /* * 0x02, VTVoutEna * 0x04, Vintcore12Ena @@ -2374,44 +1193,29 @@ static struct ab8500_reg_init ab9540_reg_init[] = { * 0x40, Vintcore12LP * 0x80, VTVoutLP */ - REG_INIT(AB9540_REGUMISC1, 0x03, 0x80, 0xfe), + REG_INIT(AB8500_REGUMISC1, 0x03, 0x80, 0xfe), /* * 0x02, VaudioEna * 0x04, VdmicEna * 0x08, Vamic1Ena * 0x10, Vamic2Ena */ - REG_INIT(AB9540_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), + REG_INIT(AB8500_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), /* * 0x01, Vamic1_dzout * 0x02, Vamic2_dzout */ - REG_INIT(AB9540_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), - /* - * 0x03, Vsmps1Regu - * 0x0c, Vsmps1SelCtrl - * 0x10, Vsmps1AutoMode - * 0x20, Vsmps1PWMMode - */ - REG_INIT(AB9540_VSMPS1REGU, 0x04, 0x03, 0x3f), - /* - * 0x03, Vsmps2Regu - * 0x0c, Vsmps2SelCtrl - * 0x10, Vsmps2AutoMode - * 0x20, Vsmps2PWMMode - */ - REG_INIT(AB9540_VSMPS2REGU, 0x04, 0x04, 0x3f), + REG_INIT(AB8500_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), /* - * 0x03, Vsmps3Regu - * 0x0c, Vsmps3SelCtrl - * NOTE! PRCMU register + * 0x03, VpllRegu (NOTE! PRCMU register bits) + * 0x0c, VanaRegu */ - REG_INIT(AB9540_VSMPS3REGU, 0x04, 0x05, 0x0f), + REG_INIT(AB8500_VPLLVANAREGU, 0x04, 0x06, 0x0f), /* - * 0x03, VpllRegu - * 0x0c, VanaRegu + * 0x01, VrefDDREna + * 0x02, VrefDDRSleepMode */ - REG_INIT(AB9540_VPLLVANAREGU, 0x04, 0x06, 0x0f), + REG_INIT(AB8500_VREFDDR, 0x04, 0x07, 0x03), /* * 0x03, VextSupply1Regu * 0x0c, VextSupply2Regu @@ -2419,83 +1223,33 @@ static struct ab8500_reg_init ab9540_reg_init[] = { * 0x40, ExtSupply2Bypass * 0x80, ExtSupply3Bypass */ - REG_INIT(AB9540_EXTSUPPLYREGU, 0x04, 0x08, 0xff), + REG_INIT(AB8500_EXTSUPPLYREGU, 0x04, 0x08, 0xff), /* * 0x03, Vaux1Regu * 0x0c, Vaux2Regu */ - REG_INIT(AB9540_VAUX12REGU, 0x04, 0x09, 0x0f), + REG_INIT(AB8500_VAUX12REGU, 0x04, 0x09, 0x0f), /* - * 0x0c, Vrf1Regu * 0x03, Vaux3Regu */ - REG_INIT(AB9540_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f), - /* - * 0x3f, Vsmps1Sel1 - */ - REG_INIT(AB9540_VSMPS1SEL1, 0x04, 0x13, 0x3f), - /* - * 0x3f, Vsmps1Sel2 - */ - REG_INIT(AB9540_VSMPS1SEL2, 0x04, 0x14, 0x3f), - /* - * 0x3f, Vsmps1Sel3 - */ - REG_INIT(AB9540_VSMPS1SEL3, 0x04, 0x15, 0x3f), - /* - * 0x3f, Vsmps2Sel1 - */ - REG_INIT(AB9540_VSMPS2SEL1, 0x04, 0x17, 0x3f), - /* - * 0x3f, Vsmps2Sel2 - */ - REG_INIT(AB9540_VSMPS2SEL2, 0x04, 0x18, 0x3f), - /* - * 0x3f, Vsmps2Sel3 - */ - REG_INIT(AB9540_VSMPS2SEL3, 0x04, 0x19, 0x3f), - /* - * 0x7f, Vsmps3Sel1 - * NOTE! PRCMU register - */ - REG_INIT(AB9540_VSMPS3SEL1, 0x04, 0x1b, 0x7f), - /* - * 0x7f, Vsmps3Sel2 - * NOTE! PRCMU register - */ - REG_INIT(AB9540_VSMPS3SEL2, 0x04, 0x1c, 0x7f), + REG_INIT(AB8500_VRF1VAUX3REGU, 0x04, 0x0a, 0x03), /* * 0x0f, Vaux1Sel */ - REG_INIT(AB9540_VAUX1SEL, 0x04, 0x1f, 0x0f), + REG_INIT(AB8500_VAUX1SEL, 0x04, 0x1f, 0x0f), /* * 0x0f, Vaux2Sel */ - REG_INIT(AB9540_VAUX2SEL, 0x04, 0x20, 0x0f), + REG_INIT(AB8500_VAUX2SEL, 0x04, 0x20, 0x0f), /* * 0x07, Vaux3Sel - * 0x30, Vrf1Sel */ - REG_INIT(AB9540_VRF1VAUX3SEL, 0x04, 0x21, 0x37), + REG_INIT(AB8500_VRF1VAUX3SEL, 0x04, 0x21, 0x07), /* * 0x01, VextSupply12LP */ - REG_INIT(AB9540_REGUCTRL2SPARE, 0x04, 0x22, 0x01), - /* - * 0x03, Vaux4RequestCtrl - */ - REG_INIT(AB9540_VAUX4REQCTRL, 0x04, 0x2d, 0x03), - /* - * 0x03, Vaux4Regu - */ - REG_INIT(AB9540_VAUX4REGU, 0x04, 0x2e, 0x03), - /* - * 0x08, Vaux4Sel - */ - REG_INIT(AB9540_VAUX4SEL, 0x04, 0x2f, 0x0f), + REG_INIT(AB8500_REGUCTRL2SPARE, 0x04, 0x22, 0x01), /* - * 0x01, VpllDisch - * 0x02, Vrf1Disch * 0x04, Vaux1Disch * 0x08, Vaux2Disch * 0x10, Vaux3Disch @@ -2503,243 +1257,170 @@ static struct ab8500_reg_init ab9540_reg_init[] = { * 0x40, VTVoutDisch * 0x80, VaudioDisch */ - REG_INIT(AB9540_REGUCTRLDISCH, 0x04, 0x43, 0xff), + REG_INIT(AB8500_REGUCTRLDISCH, 0x04, 0x43, 0xfc), /* - * 0x01, VsimDisch * 0x02, VanaDisch * 0x04, VdmicPullDownEna - * 0x08, VpllPullDownEna * 0x10, VdmicDisch */ - REG_INIT(AB9540_REGUCTRLDISCH2, 0x04, 0x44, 0x1f), - /* - * 0x01, Vaux4Disch - */ - REG_INIT(AB9540_REGUCTRLDISCH3, 0x04, 0x48, 0x01), + REG_INIT(AB8500_REGUCTRLDISCH2, 0x04, 0x44, 0x16), }; -/* AB8540 register init */ -static struct ab8500_reg_init ab8540_reg_init[] = { - /* - * 0x01, VSimSycClkReq1Valid - * 0x02, VSimSycClkReq2Valid - * 0x04, VSimSycClkReq3Valid - * 0x08, VSimSycClkReq4Valid - * 0x10, VSimSycClkReq5Valid - * 0x20, VSimSycClkReq6Valid - * 0x40, VSimSycClkReq7Valid - * 0x80, VSimSycClkReq8Valid - */ - REG_INIT(AB8540_VSIMSYSCLKCTRL, 0x02, 0x33, 0xff), +/* AB8505 register init */ +static struct ab8500_reg_init ab8505_reg_init[] = { /* * 0x03, VarmRequestCtrl - * 0x0c, VapeRequestCtrl - * 0x30, Vsmps1RequestCtrl - * 0xc0, Vsmps2RequestCtrl + * 0x0c, VsmpsCRequestCtrl + * 0x30, VsmpsARequestCtrl + * 0xc0, VsmpsBRequestCtrl */ - REG_INIT(AB8540_REGUREQUESTCTRL1, 0x03, 0x03, 0xff), + REG_INIT(AB8505_REGUREQUESTCTRL1, 0x03, 0x03, 0xff), /* - * 0x03, Vsmps3RequestCtrl + * 0x03, VsafeRequestCtrl * 0x0c, VpllRequestCtrl * 0x30, VanaRequestCtrl - * 0xc0, VextSupply1RequestCtrl */ - REG_INIT(AB8540_REGUREQUESTCTRL2, 0x03, 0x04, 0xff), + REG_INIT(AB8505_REGUREQUESTCTRL2, 0x03, 0x04, 0x3f), /* - * 0x03, VextSupply2RequestCtrl - * 0x0c, VextSupply3RequestCtrl * 0x30, Vaux1RequestCtrl * 0xc0, Vaux2RequestCtrl */ - REG_INIT(AB8540_REGUREQUESTCTRL3, 0x03, 0x05, 0xff), + REG_INIT(AB8505_REGUREQUESTCTRL3, 0x03, 0x05, 0xf0), /* * 0x03, Vaux3RequestCtrl * 0x04, SwHPReq */ - REG_INIT(AB8540_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), + REG_INIT(AB8505_REGUREQUESTCTRL4, 0x03, 0x06, 0x07), /* - * 0x01, Vsmps1SysClkReq1HPValid - * 0x02, Vsmps2SysClkReq1HPValid - * 0x04, Vsmps3SysClkReq1HPValid + * 0x01, VsmpsASysClkReq1HPValid + * 0x02, VsmpsBSysClkReq1HPValid + * 0x04, VsafeSysClkReq1HPValid * 0x08, VanaSysClkReq1HPValid * 0x10, VpllSysClkReq1HPValid * 0x20, Vaux1SysClkReq1HPValid * 0x40, Vaux2SysClkReq1HPValid * 0x80, Vaux3SysClkReq1HPValid */ - REG_INIT(AB8540_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff), + REG_INIT(AB8505_REGUSYSCLKREQ1HPVALID1, 0x03, 0x07, 0xff), /* - * 0x01, VapeSysClkReq1HPValid + * 0x01, VsmpsCSysClkReq1HPValid * 0x02, VarmSysClkReq1HPValid * 0x04, VbbSysClkReq1HPValid - * 0x10, VextSupply1SysClkReq1HPValid - * 0x20, VextSupply2SysClkReq1HPValid - * 0x40, VextSupply3SysClkReq1HPValid + * 0x08, VsmpsMSysClkReq1HPValid */ - REG_INIT(AB8540_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x77), + REG_INIT(AB8505_REGUSYSCLKREQ1HPVALID2, 0x03, 0x08, 0x0f), /* - * 0x01, Vsmps1HwHPReq1Valid - * 0x02, Vsmps2HwHPReq1Valid - * 0x04, Vsmps3HwHPReq1Valid + * 0x01, VsmpsAHwHPReq1Valid + * 0x02, VsmpsBHwHPReq1Valid + * 0x04, VsafeHwHPReq1Valid * 0x08, VanaHwHPReq1Valid * 0x10, VpllHwHPReq1Valid * 0x20, Vaux1HwHPReq1Valid * 0x40, Vaux2HwHPReq1Valid * 0x80, Vaux3HwHPReq1Valid */ - REG_INIT(AB8540_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff), + REG_INIT(AB8505_REGUHWHPREQ1VALID1, 0x03, 0x09, 0xff), /* - * 0x01, VextSupply1HwHPReq1Valid - * 0x02, VextSupply2HwHPReq1Valid - * 0x04, VextSupply3HwHPReq1Valid + * 0x08, VsmpsMHwHPReq1Valid */ - REG_INIT(AB8540_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x07), + REG_INIT(AB8505_REGUHWHPREQ1VALID2, 0x03, 0x0a, 0x08), /* - * 0x01, Vsmps1HwHPReq2Valid - * 0x02, Vsmps2HwHPReq2Valid - * 0x03, Vsmps3HwHPReq2Valid + * 0x01, VsmpsAHwHPReq2Valid + * 0x02, VsmpsBHwHPReq2Valid + * 0x04, VsafeHwHPReq2Valid * 0x08, VanaHwHPReq2Valid * 0x10, VpllHwHPReq2Valid * 0x20, Vaux1HwHPReq2Valid * 0x40, Vaux2HwHPReq2Valid * 0x80, Vaux3HwHPReq2Valid */ - REG_INIT(AB8540_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff), + REG_INIT(AB8505_REGUHWHPREQ2VALID1, 0x03, 0x0b, 0xff), /* - * 0x01, VextSupply1HwHPReq2Valid - * 0x02, VextSupply2HwHPReq2Valid - * 0x04, VextSupply3HwHPReq2Valid + * 0x08, VsmpsMHwHPReq2Valid */ - REG_INIT(AB8540_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x07), + REG_INIT(AB8505_REGUHWHPREQ2VALID2, 0x03, 0x0c, 0x08), /* - * 0x01, VapeSwHPReqValid + * 0x01, VsmpsCSwHPReqValid * 0x02, VarmSwHPReqValid - * 0x04, Vsmps1SwHPReqValid - * 0x08, Vsmps2SwHPReqValid - * 0x10, Vsmps3SwHPReqValid + * 0x04, VsmpsASwHPReqValid + * 0x08, VsmpsBSwHPReqValid + * 0x10, VsafeSwHPReqValid * 0x20, VanaSwHPReqValid * 0x40, VpllSwHPReqValid * 0x80, Vaux1SwHPReqValid */ - REG_INIT(AB8540_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff), + REG_INIT(AB8505_REGUSWHPREQVALID1, 0x03, 0x0d, 0xff), /* * 0x01, Vaux2SwHPReqValid * 0x02, Vaux3SwHPReqValid - * 0x04, VextSupply1SwHPReqValid - * 0x08, VextSupply2SwHPReqValid - * 0x10, VextSupply3SwHPReqValid + * 0x20, VsmpsMSwHPReqValid */ - REG_INIT(AB8540_REGUSWHPREQVALID2, 0x03, 0x0e, 0x1f), + REG_INIT(AB8505_REGUSWHPREQVALID2, 0x03, 0x0e, 0x23), /* * 0x02, SysClkReq2Valid1 - * ... - * 0x80, SysClkReq8Valid1 + * 0x04, SysClkReq3Valid1 + * 0x08, SysClkReq4Valid1 */ - REG_INIT(AB8540_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0xff), + REG_INIT(AB8505_REGUSYSCLKREQVALID1, 0x03, 0x0f, 0x0e), /* * 0x02, SysClkReq2Valid2 - * ... - * 0x80, SysClkReq8Valid2 + * 0x04, SysClkReq3Valid2 + * 0x08, SysClkReq4Valid2 */ - REG_INIT(AB8540_REGUSYSCLKREQVALID2, 0x03, 0x10, 0xff), + REG_INIT(AB8505_REGUSYSCLKREQVALID2, 0x03, 0x10, 0x0e), /* * 0x01, Vaux4SwHPReqValid * 0x02, Vaux4HwHPReq2Valid * 0x04, Vaux4HwHPReq1Valid * 0x08, Vaux4SysClkReq1HPValid */ - REG_INIT(AB8540_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f), - /* - * 0x01, Vaux5SwHPReqValid - * 0x02, Vaux5HwHPReq2Valid - * 0x04, Vaux5HwHPReq1Valid - * 0x08, Vaux5SysClkReq1HPValid - */ - REG_INIT(AB8540_REGUVAUX5REQVALID, 0x03, 0x12, 0x0f), - /* - * 0x01, Vaux6SwHPReqValid - * 0x02, Vaux6HwHPReq2Valid - * 0x04, Vaux6HwHPReq1Valid - * 0x08, Vaux6SysClkReq1HPValid - */ - REG_INIT(AB8540_REGUVAUX6REQVALID, 0x03, 0x13, 0x0f), - /* - * 0x01, VclkbSwHPReqValid - * 0x02, VclkbHwHPReq2Valid - * 0x04, VclkbHwHPReq1Valid - * 0x08, VclkbSysClkReq1HPValid - */ - REG_INIT(AB8540_REGUVCLKBREQVALID, 0x03, 0x14, 0x0f), - /* - * 0x01, Vrf1SwHPReqValid - * 0x02, Vrf1HwHPReq2Valid - * 0x04, Vrf1HwHPReq1Valid - * 0x08, Vrf1SysClkReq1HPValid - */ - REG_INIT(AB8540_REGUVRF1REQVALID, 0x03, 0x15, 0x0f), + REG_INIT(AB8505_REGUVAUX4REQVALID, 0x03, 0x11, 0x0f), /* - * 0x02, VTVoutEna - * 0x04, Vintcore12Ena - * 0x38, Vintcore12Sel - * 0x40, Vintcore12LP - * 0x80, VTVoutLP + * 0x02, VadcEna + * 0x04, VintCore12Ena + * 0x38, VintCore12Sel + * 0x40, VintCore12LP + * 0x80, VadcLP */ - REG_INIT(AB8540_REGUMISC1, 0x03, 0x80, 0xfe), + REG_INIT(AB8505_REGUMISC1, 0x03, 0x80, 0xfe), /* * 0x02, VaudioEna * 0x04, VdmicEna * 0x08, Vamic1Ena * 0x10, Vamic2Ena - * 0x20, Vamic12LP - * 0xC0, VdmicSel */ - REG_INIT(AB8540_VAUDIOSUPPLY, 0x03, 0x83, 0xfe), + REG_INIT(AB8505_VAUDIOSUPPLY, 0x03, 0x83, 0x1e), /* * 0x01, Vamic1_dzout * 0x02, Vamic2_dzout */ - REG_INIT(AB8540_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), - /* - * 0x07, VHSICSel - * 0x08, VHSICOffState - * 0x10, VHSIEna - * 0x20, VHSICLP - */ - REG_INIT(AB8540_VHSIC, 0x03, 0x87, 0x3f), - /* - * 0x07, VSDIOSel - * 0x08, VSDIOOffState - * 0x10, VSDIOEna - * 0x20, VSDIOLP - */ - REG_INIT(AB8540_VSDIO, 0x03, 0x88, 0x3f), + REG_INIT(AB8505_REGUCTRL1VAMIC, 0x03, 0x84, 0x03), /* - * 0x03, Vsmps1Regu - * 0x0c, Vsmps1SelCtrl - * 0x10, Vsmps1AutoMode - * 0x20, Vsmps1PWMMode + * 0x03, VsmpsARegu + * 0x0c, VsmpsASelCtrl + * 0x10, VsmpsAAutoMode + * 0x20, VsmpsAPWMMode */ - REG_INIT(AB8540_VSMPS1REGU, 0x04, 0x03, 0x3f), + REG_INIT(AB8505_VSMPSAREGU, 0x04, 0x03, 0x3f), /* - * 0x03, Vsmps2Regu - * 0x0c, Vsmps2SelCtrl - * 0x10, Vsmps2AutoMode - * 0x20, Vsmps2PWMMode + * 0x03, VsmpsBRegu + * 0x0c, VsmpsBSelCtrl + * 0x10, VsmpsBAutoMode + * 0x20, VsmpsBPWMMode */ - REG_INIT(AB8540_VSMPS2REGU, 0x04, 0x04, 0x3f), + REG_INIT(AB8505_VSMPSBREGU, 0x04, 0x04, 0x3f), /* - * 0x03, Vsmps3Regu - * 0x0c, Vsmps3SelCtrl - * 0x10, Vsmps3AutoMode - * 0x20, Vsmps3PWMMode - * NOTE! PRCMU register + * 0x03, VsafeRegu + * 0x0c, VsafeSelCtrl + * 0x10, VsafeAutoMode + * 0x20, VsafePWMMode */ - REG_INIT(AB8540_VSMPS3REGU, 0x04, 0x05, 0x0f), + REG_INIT(AB8505_VSAFEREGU, 0x04, 0x05, 0x3f), /* - * 0x03, VpllRegu + * 0x03, VpllRegu (NOTE! PRCMU register bits) * 0x0c, VanaRegu */ - REG_INIT(AB8540_VPLLVANAREGU, 0x04, 0x06, 0x0f), + REG_INIT(AB8505_VPLLVANAREGU, 0x04, 0x06, 0x0f), /* * 0x03, VextSupply1Regu * 0x0c, VextSupply2Regu @@ -2747,128 +1428,78 @@ static struct ab8500_reg_init ab8540_reg_init[] = { * 0x40, ExtSupply2Bypass * 0x80, ExtSupply3Bypass */ - REG_INIT(AB8540_EXTSUPPLYREGU, 0x04, 0x08, 0xff), + REG_INIT(AB8505_EXTSUPPLYREGU, 0x04, 0x08, 0xff), /* * 0x03, Vaux1Regu * 0x0c, Vaux2Regu */ - REG_INIT(AB8540_VAUX12REGU, 0x04, 0x09, 0x0f), + REG_INIT(AB8505_VAUX12REGU, 0x04, 0x09, 0x0f), /* - * 0x0c, VRF1Regu - * 0x03, Vaux3Regu + * 0x0f, Vaux3Regu + */ + REG_INIT(AB8505_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f), + /* + * 0x3f, VsmpsASel1 */ - REG_INIT(AB8540_VRF1VAUX3REGU, 0x04, 0x0a, 0x0f), + REG_INIT(AB8505_VSMPSASEL1, 0x04, 0x13, 0x3f), /* - * 0x3f, Vsmps1Sel1 + * 0x3f, VsmpsASel2 */ - REG_INIT(AB8540_VSMPS1SEL1, 0x04, 0x13, 0x3f), + REG_INIT(AB8505_VSMPSASEL2, 0x04, 0x14, 0x3f), /* - * 0x3f, Vsmps1Sel2 + * 0x3f, VsmpsASel3 */ - REG_INIT(AB8540_VSMPS1SEL2, 0x04, 0x14, 0x3f), + REG_INIT(AB8505_VSMPSASEL3, 0x04, 0x15, 0x3f), /* - * 0x3f, Vsmps1Sel3 + * 0x3f, VsmpsBSel1 */ - REG_INIT(AB8540_VSMPS1SEL3, 0x04, 0x15, 0x3f), + REG_INIT(AB8505_VSMPSBSEL1, 0x04, 0x17, 0x3f), /* - * 0x3f, Vsmps2Sel1 + * 0x3f, VsmpsBSel2 */ - REG_INIT(AB8540_VSMPS2SEL1, 0x04, 0x17, 0x3f), + REG_INIT(AB8505_VSMPSBSEL2, 0x04, 0x18, 0x3f), /* - * 0x3f, Vsmps2Sel2 + * 0x3f, VsmpsBSel3 */ - REG_INIT(AB8540_VSMPS2SEL2, 0x04, 0x18, 0x3f), + REG_INIT(AB8505_VSMPSBSEL3, 0x04, 0x19, 0x3f), /* - * 0x3f, Vsmps2Sel3 + * 0x7f, VsafeSel1 */ - REG_INIT(AB8540_VSMPS2SEL3, 0x04, 0x19, 0x3f), + REG_INIT(AB8505_VSAFESEL1, 0x04, 0x1b, 0x7f), /* - * 0x7f, Vsmps3Sel1 - * NOTE! PRCMU register + * 0x3f, VsafeSel2 */ - REG_INIT(AB8540_VSMPS3SEL1, 0x04, 0x1b, 0x7f), + REG_INIT(AB8505_VSAFESEL2, 0x04, 0x1c, 0x7f), /* - * 0x7f, Vsmps3Sel2 - * NOTE! PRCMU register + * 0x3f, VsafeSel3 */ - REG_INIT(AB8540_VSMPS3SEL2, 0x04, 0x1c, 0x7f), + REG_INIT(AB8505_VSAFESEL3, 0x04, 0x1d, 0x7f), /* * 0x0f, Vaux1Sel */ - REG_INIT(AB8540_VAUX1SEL, 0x04, 0x1f, 0x0f), + REG_INIT(AB8505_VAUX1SEL, 0x04, 0x1f, 0x0f), /* * 0x0f, Vaux2Sel */ - REG_INIT(AB8540_VAUX2SEL, 0x04, 0x20, 0x0f), + REG_INIT(AB8505_VAUX2SEL, 0x04, 0x20, 0x0f), /* * 0x07, Vaux3Sel - * 0x70, Vrf1Sel - */ - REG_INIT(AB8540_VRF1VAUX3SEL, 0x04, 0x21, 0x77), - /* - * 0x01, VextSupply12LP - */ - REG_INIT(AB8540_REGUCTRL2SPARE, 0x04, 0x22, 0x01), - /* - * 0x07, Vanasel - * 0x30, Vpllsel + * 0x30, VRF1Sel */ - REG_INIT(AB8540_VANAVPLLSEL, 0x04, 0x29, 0x37), + REG_INIT(AB8505_VRF1VAUX3SEL, 0x04, 0x21, 0x37), /* * 0x03, Vaux4RequestCtrl */ - REG_INIT(AB8540_VAUX4REQCTRL, 0x04, 0x2d, 0x03), + REG_INIT(AB8505_VAUX4REQCTRL, 0x04, 0x2d, 0x03), /* * 0x03, Vaux4Regu */ - REG_INIT(AB8540_VAUX4REGU, 0x04, 0x2e, 0x03), + REG_INIT(AB8505_VAUX4REGU, 0x04, 0x2e, 0x03), /* * 0x0f, Vaux4Sel */ - REG_INIT(AB8540_VAUX4SEL, 0x04, 0x2f, 0x0f), - /* - * 0x03, Vaux5RequestCtrl - */ - REG_INIT(AB8540_VAUX5REQCTRL, 0x04, 0x31, 0x03), - /* - * 0x03, Vaux5Regu - */ - REG_INIT(AB8540_VAUX5REGU, 0x04, 0x32, 0x03), - /* - * 0x3f, Vaux5Sel - */ - REG_INIT(AB8540_VAUX5SEL, 0x04, 0x33, 0x3f), - /* - * 0x03, Vaux6RequestCtrl - */ - REG_INIT(AB8540_VAUX6REQCTRL, 0x04, 0x34, 0x03), - /* - * 0x03, Vaux6Regu - */ - REG_INIT(AB8540_VAUX6REGU, 0x04, 0x35, 0x03), - /* - * 0x3f, Vaux6Sel - */ - REG_INIT(AB8540_VAUX6SEL, 0x04, 0x36, 0x3f), - /* - * 0x03, VCLKBRequestCtrl - */ - REG_INIT(AB8540_VCLKBREQCTRL, 0x04, 0x37, 0x03), - /* - * 0x03, VCLKBRegu - */ - REG_INIT(AB8540_VCLKBREGU, 0x04, 0x38, 0x03), - /* - * 0x07, VCLKBSel - */ - REG_INIT(AB8540_VCLKBSEL, 0x04, 0x39, 0x07), - /* - * 0x03, Vrf1RequestCtrl - */ - REG_INIT(AB8540_VRF1REQCTRL, 0x04, 0x3a, 0x03), + REG_INIT(AB8505_VAUX4SEL, 0x04, 0x2f, 0x0f), /* - * 0x01, VpllDisch - * 0x02, Vrf1Disch * 0x04, Vaux1Disch * 0x08, Vaux2Disch * 0x10, Vaux3Disch @@ -2876,24 +1507,33 @@ static struct ab8500_reg_init ab8540_reg_init[] = { * 0x40, VTVoutDisch * 0x80, VaudioDisch */ - REG_INIT(AB8540_REGUCTRLDISCH, 0x04, 0x43, 0xff), + REG_INIT(AB8505_REGUCTRLDISCH, 0x04, 0x43, 0xfc), /* * 0x02, VanaDisch * 0x04, VdmicPullDownEna - * 0x08, VpllPullDownEna * 0x10, VdmicDisch */ - REG_INIT(AB8540_REGUCTRLDISCH2, 0x04, 0x44, 0x1e), + REG_INIT(AB8505_REGUCTRLDISCH2, 0x04, 0x44, 0x16), /* * 0x01, Vaux4Disch */ - REG_INIT(AB8540_REGUCTRLDISCH3, 0x04, 0x48, 0x01), + REG_INIT(AB8505_REGUCTRLDISCH3, 0x04, 0x48, 0x01), + /* + * 0x07, Vaux5Sel + * 0x08, Vaux5LP + * 0x10, Vaux5Ena + * 0x20, Vaux5Disch + * 0x40, Vaux5DisSfst + * 0x80, Vaux5DisPulld + */ + REG_INIT(AB8505_CTRLVAUX5, 0x01, 0x55, 0xff), /* - * 0x01, Vaux5Disch - * 0x02, Vaux6Disch - * 0x04, VCLKBDisch + * 0x07, Vaux6Sel + * 0x08, Vaux6LP + * 0x10, Vaux6Ena + * 0x80, Vaux6DisPulld */ - REG_INIT(AB8540_REGUCTRLDISCH4, 0x04, 0x49, 0x07), + REG_INIT(AB8505_CTRLVAUX6, 0x01, 0x56, 0x9f), }; static struct of_regulator_match ab8500_regulator_match[] = { @@ -2925,37 +1565,6 @@ static struct of_regulator_match ab8505_regulator_match[] = { { .name = "ab8500_ldo_ana", .driver_data = (void *) AB8505_LDO_ANA, }, }; -static struct of_regulator_match ab8540_regulator_match[] = { - { .name = "ab8500_ldo_aux1", .driver_data = (void *) AB8540_LDO_AUX1, }, - { .name = "ab8500_ldo_aux2", .driver_data = (void *) AB8540_LDO_AUX2, }, - { .name = "ab8500_ldo_aux3", .driver_data = (void *) AB8540_LDO_AUX3, }, - { .name = "ab8500_ldo_aux4", .driver_data = (void *) AB8540_LDO_AUX4, }, - { .name = "ab8500_ldo_aux5", .driver_data = (void *) AB8540_LDO_AUX5, }, - { .name = "ab8500_ldo_aux6", .driver_data = (void *) AB8540_LDO_AUX6, }, - { .name = "ab8500_ldo_intcore", .driver_data = (void *) AB8540_LDO_INTCORE, }, - { .name = "ab8500_ldo_tvout", .driver_data = (void *) AB8540_LDO_TVOUT, }, - { .name = "ab8500_ldo_audio", .driver_data = (void *) AB8540_LDO_AUDIO, }, - { .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB8540_LDO_ANAMIC1, }, - { .name = "ab8500_ldo_anamic2", .driver_data = (void *) AB8540_LDO_ANAMIC2, }, - { .name = "ab8500_ldo_dmic", .driver_data = (void *) AB8540_LDO_DMIC, }, - { .name = "ab8500_ldo_ana", .driver_data = (void *) AB8540_LDO_ANA, }, - { .name = "ab8500_ldo_sdio", .driver_data = (void *) AB8540_LDO_SDIO, }, -}; - -static struct of_regulator_match ab9540_regulator_match[] = { - { .name = "ab8500_ldo_aux1", .driver_data = (void *) AB9540_LDO_AUX1, }, - { .name = "ab8500_ldo_aux2", .driver_data = (void *) AB9540_LDO_AUX2, }, - { .name = "ab8500_ldo_aux3", .driver_data = (void *) AB9540_LDO_AUX3, }, - { .name = "ab8500_ldo_aux4", .driver_data = (void *) AB9540_LDO_AUX4, }, - { .name = "ab8500_ldo_intcore", .driver_data = (void *) AB9540_LDO_INTCORE, }, - { .name = "ab8500_ldo_tvout", .driver_data = (void *) AB9540_LDO_TVOUT, }, - { .name = "ab8500_ldo_audio", .driver_data = (void *) AB9540_LDO_AUDIO, }, - { .name = "ab8500_ldo_anamic1", .driver_data = (void *) AB9540_LDO_ANAMIC1, }, - { .name = "ab8500_ldo_anamic2", .driver_data = (void *) AB9540_LDO_ANAMIC2, }, - { .name = "ab8500_ldo_dmic", .driver_data = (void *) AB9540_LDO_DMIC, }, - { .name = "ab8500_ldo_ana", .driver_data = (void *) AB9540_LDO_ANA, }, -}; - static struct { struct ab8500_regulator_info *info; int info_size; @@ -2967,27 +1576,13 @@ static struct { static void abx500_get_regulator_info(struct ab8500 *ab8500) { - if (is_ab9540(ab8500)) { - abx500_regulator.info = ab9540_regulator_info; - abx500_regulator.info_size = ARRAY_SIZE(ab9540_regulator_info); - abx500_regulator.init = ab9540_reg_init; - abx500_regulator.init_size = AB9540_NUM_REGULATOR_REGISTERS; - abx500_regulator.match = ab9540_regulator_match; - abx500_regulator.match_size = ARRAY_SIZE(ab9540_regulator_match); - } else if (is_ab8505(ab8500)) { + if (is_ab8505(ab8500)) { abx500_regulator.info = ab8505_regulator_info; abx500_regulator.info_size = ARRAY_SIZE(ab8505_regulator_info); abx500_regulator.init = ab8505_reg_init; abx500_regulator.init_size = AB8505_NUM_REGULATOR_REGISTERS; abx500_regulator.match = ab8505_regulator_match; abx500_regulator.match_size = ARRAY_SIZE(ab8505_regulator_match); - } else if (is_ab8540(ab8500)) { - abx500_regulator.info = ab8540_regulator_info; - abx500_regulator.info_size = ARRAY_SIZE(ab8540_regulator_info); - abx500_regulator.init = ab8540_reg_init; - abx500_regulator.init_size = AB8540_NUM_REGULATOR_REGISTERS; - abx500_regulator.match = ab8540_regulator_match; - abx500_regulator.match_size = ARRAY_SIZE(ab8540_regulator_match); } else { abx500_regulator.info = ab8500_regulator_info; abx500_regulator.info_size = ARRAY_SIZE(ab8500_regulator_info); diff --git a/include/linux/regulator/ab8500.h b/include/linux/regulator/ab8500.h index d8ecefaf63ca..6d46f962685d 100644 --- a/include/linux/regulator/ab8500.h +++ b/include/linux/regulator/ab8500.h @@ -49,47 +49,7 @@ enum ab8505_regulator_id { AB8505_NUM_REGULATORS, }; -/* AB9540 regulators */ -enum ab9540_regulator_id { - AB9540_LDO_AUX1, - AB9540_LDO_AUX2, - AB9540_LDO_AUX3, - AB9540_LDO_AUX4, - AB9540_LDO_INTCORE, - AB9540_LDO_TVOUT, - AB9540_LDO_USB, - AB9540_LDO_AUDIO, - AB9540_LDO_ANAMIC1, - AB9540_LDO_ANAMIC2, - AB9540_LDO_DMIC, - AB9540_LDO_ANA, - AB9540_SYSCLKREQ_2, - AB9540_SYSCLKREQ_4, - AB9540_NUM_REGULATORS, -}; - -/* AB8540 regulators */ -enum ab8540_regulator_id { - AB8540_LDO_AUX1, - AB8540_LDO_AUX2, - AB8540_LDO_AUX3, - AB8540_LDO_AUX4, - AB8540_LDO_AUX5, - AB8540_LDO_AUX6, - AB8540_LDO_INTCORE, - AB8540_LDO_TVOUT, - AB8540_LDO_AUDIO, - AB8540_LDO_ANAMIC1, - AB8540_LDO_ANAMIC2, - AB8540_LDO_DMIC, - AB8540_LDO_ANA, - AB8540_LDO_SDIO, - AB8540_SYSCLKREQ_2, - AB8540_SYSCLKREQ_4, - AB8540_NUM_REGULATORS, -}; - -/* AB8500, AB8505, and AB9540 register initialization */ +/* AB8500 and AB8505 register initialization */ struct ab8500_regulator_reg_init { int id; u8 mask; @@ -185,121 +145,6 @@ enum ab8505_regulator_reg { AB8505_NUM_REGULATOR_REGISTERS, }; -/* AB9540 registers */ -enum ab9540_regulator_reg { - AB9540_REGUREQUESTCTRL1, - AB9540_REGUREQUESTCTRL2, - AB9540_REGUREQUESTCTRL3, - AB9540_REGUREQUESTCTRL4, - AB9540_REGUSYSCLKREQ1HPVALID1, - AB9540_REGUSYSCLKREQ1HPVALID2, - AB9540_REGUHWHPREQ1VALID1, - AB9540_REGUHWHPREQ1VALID2, - AB9540_REGUHWHPREQ2VALID1, - AB9540_REGUHWHPREQ2VALID2, - AB9540_REGUSWHPREQVALID1, - AB9540_REGUSWHPREQVALID2, - AB9540_REGUSYSCLKREQVALID1, - AB9540_REGUSYSCLKREQVALID2, - AB9540_REGUVAUX4REQVALID, - AB9540_REGUMISC1, - AB9540_VAUDIOSUPPLY, - AB9540_REGUCTRL1VAMIC, - AB9540_VSMPS1REGU, - AB9540_VSMPS2REGU, - AB9540_VSMPS3REGU, /* NOTE! PRCMU register */ - AB9540_VPLLVANAREGU, - AB9540_EXTSUPPLYREGU, - AB9540_VAUX12REGU, - AB9540_VRF1VAUX3REGU, - AB9540_VSMPS1SEL1, - AB9540_VSMPS1SEL2, - AB9540_VSMPS1SEL3, - AB9540_VSMPS2SEL1, - AB9540_VSMPS2SEL2, - AB9540_VSMPS2SEL3, - AB9540_VSMPS3SEL1, /* NOTE! PRCMU register */ - AB9540_VSMPS3SEL2, /* NOTE! PRCMU register */ - AB9540_VAUX1SEL, - AB9540_VAUX2SEL, - AB9540_VRF1VAUX3SEL, - AB9540_REGUCTRL2SPARE, - AB9540_VAUX4REQCTRL, - AB9540_VAUX4REGU, - AB9540_VAUX4SEL, - AB9540_REGUCTRLDISCH, - AB9540_REGUCTRLDISCH2, - AB9540_REGUCTRLDISCH3, - AB9540_NUM_REGULATOR_REGISTERS, -}; - -/* AB8540 registers */ -enum ab8540_regulator_reg { - AB8540_REGUREQUESTCTRL1, - AB8540_REGUREQUESTCTRL2, - AB8540_REGUREQUESTCTRL3, - AB8540_REGUREQUESTCTRL4, - AB8540_REGUSYSCLKREQ1HPVALID1, - AB8540_REGUSYSCLKREQ1HPVALID2, - AB8540_REGUHWHPREQ1VALID1, - AB8540_REGUHWHPREQ1VALID2, - AB8540_REGUHWHPREQ2VALID1, - AB8540_REGUHWHPREQ2VALID2, - AB8540_REGUSWHPREQVALID1, - AB8540_REGUSWHPREQVALID2, - AB8540_REGUSYSCLKREQVALID1, - AB8540_REGUSYSCLKREQVALID2, - AB8540_REGUVAUX4REQVALID, - AB8540_REGUVAUX5REQVALID, - AB8540_REGUVAUX6REQVALID, - AB8540_REGUVCLKBREQVALID, - AB8540_REGUVRF1REQVALID, - AB8540_REGUMISC1, - AB8540_VAUDIOSUPPLY, - AB8540_REGUCTRL1VAMIC, - AB8540_VHSIC, - AB8540_VSDIO, - AB8540_VSMPS1REGU, - AB8540_VSMPS2REGU, - AB8540_VSMPS3REGU, - AB8540_VPLLVANAREGU, - AB8540_EXTSUPPLYREGU, - AB8540_VAUX12REGU, - AB8540_VRF1VAUX3REGU, - AB8540_VSMPS1SEL1, - AB8540_VSMPS1SEL2, - AB8540_VSMPS1SEL3, - AB8540_VSMPS2SEL1, - AB8540_VSMPS2SEL2, - AB8540_VSMPS2SEL3, - AB8540_VSMPS3SEL1, - AB8540_VSMPS3SEL2, - AB8540_VAUX1SEL, - AB8540_VAUX2SEL, - AB8540_VRF1VAUX3SEL, - AB8540_REGUCTRL2SPARE, - AB8540_VAUX4REQCTRL, - AB8540_VAUX4REGU, - AB8540_VAUX4SEL, - AB8540_VAUX5REQCTRL, - AB8540_VAUX5REGU, - AB8540_VAUX5SEL, - AB8540_VAUX6REQCTRL, - AB8540_VAUX6REGU, - AB8540_VAUX6SEL, - AB8540_VCLKBREQCTRL, - AB8540_VCLKBREGU, - AB8540_VCLKBSEL, - AB8540_VRF1REQCTRL, - AB8540_REGUCTRLDISCH, - AB8540_REGUCTRLDISCH2, - AB8540_REGUCTRLDISCH3, - AB8540_REGUCTRLDISCH4, - AB8540_VSIMSYSCLKCTRL, - AB8540_VANAVPLLSEL, - AB8540_NUM_REGULATOR_REGISTERS, -}; - /* AB8500 external regulators */ struct ab8500_ext_regulator_cfg { bool hwreq; /* requires hw mode or high power mode */ -- cgit From 44773ba170a6f969620221a6d87d03feae5e464f Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 16 Apr 2018 10:22:01 -0700 Subject: ARM: OMAP2+: Drop unused pm-noop Looks like these functions don't do anything in the mainline kernel so we can just drop it. Note that we must now also remove ir-rx51 pdata as it relies on the dummy platform data that does not do anything. And ir-rx51 is calling a pdata callback that doesn't do anything without checking if it exists first. For configuring device specific minimal latencies, the interface to use is pm_qos_add_request(). For an example, see what was done in commit 9834ffd1ecc3 ("ASoC: omap-mcbsp: Add PM QoS support for McBSP to prevent glitches"). I've added some comments to ir-rx51 so people using it can add pm_qos support and test it. Cc: Ivaylo Dimitrov Cc: Kevin Hilman Cc: Laurent Pinchart Cc: Tomi Valkeinen Acked-by: Mauro Carvalho Chehab Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/Makefile | 1 - arch/arm/mach-omap2/display.c | 7 -- arch/arm/mach-omap2/hsmmc.c | 1 - arch/arm/mach-omap2/i2c.c | 1 - arch/arm/mach-omap2/io.c | 3 - arch/arm/mach-omap2/omap-pm-noop.c | 176 ---------------------------- arch/arm/mach-omap2/omap-pm.h | 161 ------------------------- arch/arm/mach-omap2/pdata-quirks.c | 15 --- arch/arm/mach-omap2/pm-debug.c | 5 - arch/arm/mach-omap2/pm.c | 10 +- arch/arm/mach-omap2/timer.c | 1 - arch/arm/plat-omap/Kconfig | 10 -- drivers/media/rc/ir-rx51.c | 17 +-- include/linux/platform_data/media/ir-rx51.h | 9 -- 14 files changed, 4 insertions(+), 413 deletions(-) delete mode 100644 arch/arm/mach-omap2/omap-pm-noop.c delete mode 100644 arch/arm/mach-omap2/omap-pm.h delete mode 100644 include/linux/platform_data/media/ir-rx51.h (limited to 'include/linux') diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile index 4603c30fef73..95b64b2940e9 100644 --- a/arch/arm/mach-omap2/Makefile +++ b/arch/arm/mach-omap2/Makefile @@ -78,7 +78,6 @@ endif omap-4-5-pm-common = omap-mpuss-lowpower.o obj-$(CONFIG_ARCH_OMAP4) += $(omap-4-5-pm-common) obj-$(CONFIG_SOC_OMAP5) += $(omap-4-5-pm-common) -obj-$(CONFIG_OMAP_PM_NOOP) += omap-pm-noop.o ifeq ($(CONFIG_PM),y) obj-$(CONFIG_ARCH_OMAP2) += pm24xx.o diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index b3f6eb5d04a2..480a2b989908 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -32,7 +32,6 @@ #include #include "omap_hwmod.h" #include "omap_device.h" -#include "omap-pm.h" #include "common.h" #include "soc.h" @@ -126,11 +125,6 @@ static void omap_dsi_disable_pads(int dsi_id, unsigned lane_mask) omap4_dsi_mux_pads(dsi_id, 0); } -static int omap_dss_set_min_bus_tput(struct device *dev, unsigned long tput) -{ - return omap_pm_set_min_bus_tput(dev, OCP_INITIATOR_AGENT, tput); -} - static enum omapdss_version __init omap_display_get_version(void) { if (cpu_is_omap24xx()) @@ -169,7 +163,6 @@ static int __init omapdss_init_fbdev(void) static struct omap_dss_board_info board_data = { .dsi_enable_pads = omap_dsi_enable_pads, .dsi_disable_pads = omap_dsi_disable_pads, - .set_min_bus_tput = omap_dss_set_min_bus_tput, }; struct device_node *node; int r; diff --git a/arch/arm/mach-omap2/hsmmc.c b/arch/arm/mach-omap2/hsmmc.c index b064066d431c..0103548b0b15 100644 --- a/arch/arm/mach-omap2/hsmmc.c +++ b/arch/arm/mach-omap2/hsmmc.c @@ -18,7 +18,6 @@ #include "soc.h" #include "omap_device.h" -#include "omap-pm.h" #include "hsmmc.h" #include "control.h" diff --git a/arch/arm/mach-omap2/i2c.c b/arch/arm/mach-omap2/i2c.c index 91a21c3923b2..37ff25ee3d89 100644 --- a/arch/arm/mach-omap2/i2c.c +++ b/arch/arm/mach-omap2/i2c.c @@ -22,7 +22,6 @@ #include "soc.h" #include "omap_hwmod.h" #include "omap_device.h" -#include "omap-pm.h" #include "prm.h" #include "common.h" diff --git a/arch/arm/mach-omap2/io.c b/arch/arm/mach-omap2/io.c index cf546dfe3b32..6ce60a478409 100644 --- a/arch/arm/mach-omap2/io.c +++ b/arch/arm/mach-omap2/io.c @@ -37,7 +37,6 @@ #include "clock.h" #include "clock2xxx.h" #include "clock3xxx.h" -#include "omap-pm.h" #include "sdrc.h" #include "control.h" #include "serial.h" @@ -421,8 +420,6 @@ static void __init __maybe_unused omap_hwmod_init_postsetup(void) postsetup_state = _HWMOD_STATE_ENABLED; #endif omap_hwmod_for_each(_set_hwmod_postsetup_state, &postsetup_state); - - omap_pm_if_early_init(); } static void __init __maybe_unused omap_common_late_init(void) diff --git a/arch/arm/mach-omap2/omap-pm-noop.c b/arch/arm/mach-omap2/omap-pm-noop.c deleted file mode 100644 index 4ead077ea4e7..000000000000 --- a/arch/arm/mach-omap2/omap-pm-noop.c +++ /dev/null @@ -1,176 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * omap-pm-noop.c - OMAP power management interface - dummy version - * - * This code implements the OMAP power management interface to - * drivers, CPUIdle, CPUFreq, and DSP Bridge. It is strictly for - * debug/demonstration use, as it does nothing but printk() whenever a - * function is called (when DEBUG is defined, below) - * - * Copyright (C) 2008-2009 Texas Instruments, Inc. - * Copyright (C) 2008-2009 Nokia Corporation - * Paul Walmsley - * - * Interface developed by (in alphabetical order): - * Karthik Dasu, Tony Lindgren, Rajendra Nayak, Sakari Poussa, Veeramanikandan - * Raju, Anand Sawant, Igor Stoppa, Paul Walmsley, Richard Woodruff - */ - -#undef DEBUG - -#include -#include -#include -#include - -#include "omap_device.h" -#include "omap-pm.h" - -static bool off_mode_enabled; -static int dummy_context_loss_counter; - -/* - * Device-driver-originated constraints (via board-*.c files) - */ - -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t) -{ - if (!dev || t < -1) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - } - - if (t == -1) - pr_debug("OMAP PM: remove max MPU wakeup latency constraint: dev %s\n", - dev_name(dev)); - else - pr_debug("OMAP PM: add max MPU wakeup latency constraint: dev %s, t = %ld usec\n", - dev_name(dev), t); - - /* - * For current Linux, this needs to map the MPU to a - * powerdomain, then go through the list of current max lat - * constraints on the MPU and find the smallest. If - * the latency constraint has changed, the code should - * recompute the state to enter for the next powerdomain - * state. - * - * TI CDP code can call constraint_set here. - */ - - return 0; -} - -int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r) -{ - if (!dev || (agent_id != OCP_INITIATOR_AGENT && - agent_id != OCP_TARGET_AGENT)) { - WARN(1, "OMAP PM: %s: invalid parameter(s)", __func__); - return -EINVAL; - } - - if (r == 0) - pr_debug("OMAP PM: remove min bus tput constraint: dev %s for agent_id %d\n", - dev_name(dev), agent_id); - else - pr_debug("OMAP PM: add min bus tput constraint: dev %s for agent_id %d: rate %ld KiB\n", - dev_name(dev), agent_id, r); - - /* - * This code should model the interconnect and compute the - * required clock frequency, convert that to a VDD2 OPP ID, then - * set the VDD2 OPP appropriately. - * - * TI CDP code can call constraint_set here on the VDD2 OPP. - */ - - return 0; -} - -/* - * DSP Bridge-specific constraints - */ - - -/** - * omap_pm_enable_off_mode - notify OMAP PM that off-mode is enabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been enabled. - */ -void omap_pm_enable_off_mode(void) -{ - off_mode_enabled = true; -} - -/** - * omap_pm_disable_off_mode - notify OMAP PM that off-mode is disabled - * - * Intended for use only by OMAP PM core code to notify this layer - * that off mode has been disabled. - */ -void omap_pm_disable_off_mode(void) -{ - off_mode_enabled = false; -} - -/* - * Device context loss tracking - */ - -#ifdef CONFIG_ARCH_OMAP2PLUS - -int omap_pm_get_dev_context_loss_count(struct device *dev) -{ - struct platform_device *pdev = to_platform_device(dev); - int count; - - if (WARN_ON(!dev)) - return -ENODEV; - - if (dev->pm_domain == &omap_device_pm_domain) { - count = omap_device_get_context_loss_count(pdev); - } else { - WARN_ONCE(off_mode_enabled, "omap_pm: using dummy context loss counter; device %s should be converted to omap_device", - dev_name(dev)); - - count = dummy_context_loss_counter; - - if (off_mode_enabled) { - count++; - /* - * Context loss count has to be a non-negative value. - * Clear the sign bit to get a value range from 0 to - * INT_MAX. - */ - count &= INT_MAX; - dummy_context_loss_counter = count; - } - } - - pr_debug("OMAP PM: context loss count for dev %s = %d\n", - dev_name(dev), count); - - return count; -} - -#else - -int omap_pm_get_dev_context_loss_count(struct device *dev) -{ - return dummy_context_loss_counter; -} - -#endif - -/* Should be called before clk framework init */ -int __init omap_pm_if_early_init(void) -{ - return 0; -} - -/* Must be called after clock framework is initialized */ -int __init omap_pm_if_init(void) -{ - return 0; -} diff --git a/arch/arm/mach-omap2/omap-pm.h b/arch/arm/mach-omap2/omap-pm.h deleted file mode 100644 index 5ba5df47f91b..000000000000 --- a/arch/arm/mach-omap2/omap-pm.h +++ /dev/null @@ -1,161 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * omap-pm.h - OMAP power management interface - * - * Copyright (C) 2008-2010 Texas Instruments, Inc. - * Copyright (C) 2008-2010 Nokia Corporation - * Paul Walmsley - * - * Interface developed by (in alphabetical order): Karthik Dasu, Jouni - * Högander, Tony Lindgren, Rajendra Nayak, Sakari Poussa, - * Veeramanikandan Raju, Anand Sawant, Igor Stoppa, Paul Walmsley, - * Richard Woodruff - */ - -#ifndef ASM_ARM_ARCH_OMAP_OMAP_PM_H -#define ASM_ARM_ARCH_OMAP_OMAP_PM_H - -#include -#include -#include -#include - -/* - * agent_id values for use with omap_pm_set_min_bus_tput(): - * - * OCP_INITIATOR_AGENT is only valid for devices that can act as - * initiators -- it represents the device's L3 interconnect - * connection. OCP_TARGET_AGENT represents the device's L4 - * interconnect connection. - */ -#define OCP_TARGET_AGENT 1 -#define OCP_INITIATOR_AGENT 2 - -/** - * omap_pm_if_early_init - OMAP PM init code called before clock fw init - * @mpu_opp_table: array ptr to struct omap_opp for MPU - * @dsp_opp_table: array ptr to struct omap_opp for DSP - * @l3_opp_table : array ptr to struct omap_opp for CORE - * - * Initialize anything that must be configured before the clock - * framework starts. The "_if_" is to avoid name collisions with the - * PM idle-loop code. - */ -int __init omap_pm_if_early_init(void); - -/** - * omap_pm_if_init - OMAP PM init code called after clock fw init - * - * The main initialization code. OPP tables are passed in here. The - * "_if_" is to avoid name collisions with the PM idle-loop code. - */ -int __init omap_pm_if_init(void); - -/* - * Device-driver-originated constraints (via board-*.c files, platform_data) - */ - - -/** - * omap_pm_set_max_mpu_wakeup_lat - set the maximum MPU wakeup latency - * @dev: struct device * requesting the constraint - * @t: maximum MPU wakeup latency in microseconds - * - * Request that the maximum interrupt latency for the MPU to be no - * greater than @t microseconds. "Interrupt latency" in this case is - * defined as the elapsed time from the occurrence of a hardware or - * timer interrupt to the time when the device driver's interrupt - * service routine has been entered by the MPU. - * - * It is intended that underlying PM code will use this information to - * determine what power state to put the MPU powerdomain into, and - * possibly the CORE powerdomain as well, since interrupt handling - * code currently runs from SDRAM. Advanced PM or board*.c code may - * also configure interrupt controller priorities, OCP bus priorities, - * CPU speed(s), etc. - * - * This function will not affect device wakeup latency, e.g., time - * elapsed from when a device driver enables a hardware device with - * clk_enable(), to when the device is ready for register access or - * other use. To control this device wakeup latency, use - * omap_pm_set_max_dev_wakeup_lat() - * - * Multiple calls to omap_pm_set_max_mpu_wakeup_lat() will replace the - * previous t value. To remove the latency target for the MPU, call - * with t = -1. - * - * XXX This constraint will be deprecated soon in favor of the more - * general omap_pm_set_max_dev_wakeup_lat() - * - * Returns -EINVAL for an invalid argument, -ERANGE if the constraint - * is not satisfiable, or 0 upon success. - */ -int omap_pm_set_max_mpu_wakeup_lat(struct device *dev, long t); - - -/** - * omap_pm_set_min_bus_tput - set minimum bus throughput needed by device - * @dev: struct device * requesting the constraint - * @tbus_id: interconnect to operate on (OCP_{INITIATOR,TARGET}_AGENT) - * @r: minimum throughput (in KiB/s) - * - * Request that the minimum data throughput on the OCP interconnect - * attached to device @dev interconnect agent @tbus_id be no less - * than @r KiB/s. - * - * It is expected that the OMAP PM or bus code will use this - * information to set the interconnect clock to run at the lowest - * possible speed that satisfies all current system users. The PM or - * bus code will adjust the estimate based on its model of the bus, so - * device driver authors should attempt to specify an accurate - * quantity for their device use case, and let the PM or bus code - * overestimate the numbers as necessary to handle request/response - * latency, other competing users on the system, etc. On OMAP2/3, if - * a driver requests a minimum L4 interconnect speed constraint, the - * code will also need to add an minimum L3 interconnect speed - * constraint, - * - * Multiple calls to omap_pm_set_min_bus_tput() will replace the - * previous rate value for this device. To remove the interconnect - * throughput restriction for this device, call with r = 0. - * - * Returns -EINVAL for an invalid argument, -ERANGE if the constraint - * is not satisfiable, or 0 upon success. - */ -int omap_pm_set_min_bus_tput(struct device *dev, u8 agent_id, unsigned long r); - - -/* - * CPUFreq-originated constraint - * - * In the future, this should be handled by custom OPP clocktype - * functions. - */ - - -/* - * Device context loss tracking - */ - -/** - * omap_pm_get_dev_context_loss_count - return count of times dev has lost ctx - * @dev: struct device * - * - * This function returns the number of times that the device @dev has - * lost its internal context. This generally occurs on a powerdomain - * transition to OFF. Drivers use this as an optimization to avoid restoring - * context if the device hasn't lost it. To use, drivers should initially - * call this in their context save functions and store the result. Early in - * the driver's context restore function, the driver should call this function - * again, and compare the result to the stored counter. If they differ, the - * driver must restore device context. If the number of context losses - * exceeds the maximum positive integer, the function will wrap to 0 and - * continue counting. Returns the number of context losses for this device, - * or negative value upon error. - */ -int omap_pm_get_dev_context_loss_count(struct device *dev); - -void omap_pm_enable_off_mode(void); -void omap_pm_disable_off_mode(void); - -#endif diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 6459816c2879..7f02743edbe4 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -26,14 +26,12 @@ #include #include #include -#include #include #include "common.h" #include "common-board-devices.h" #include "control.h" #include "omap_device.h" -#include "omap-pm.h" #include "omap-secure.h" #include "soc.h" #include "hsmmc.h" @@ -514,18 +512,6 @@ void omap_auxdata_legacy_init(struct device *dev) dev->platform_data = &twl_gpio_auxdata; } -static struct ir_rx51_platform_data __maybe_unused rx51_ir_data = { - .set_max_mpu_wakeup_lat = omap_pm_set_max_mpu_wakeup_lat, -}; - -static struct platform_device __maybe_unused rx51_ir_device = { - .name = "ir_rx51", - .id = -1, - .dev = { - .platform_data = &rx51_ir_data, - }, -}; - #if IS_ENABLED(CONFIG_SND_OMAP_SOC_MCBSP) static struct omap_mcbsp_platform_data mcbsp_pdata; static void __init omap3_mcbsp_init(void) @@ -569,7 +555,6 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = { "480c9000.smartreflex", &omap_sr_pdata[OMAP_SR_MPU]), OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x4809c000, "4809c000.mmc", &mmc_pdata[0]), OF_DEV_AUXDATA("ti,omap3-hsmmc", 0x480b4000, "480b4000.mmc", &mmc_pdata[1]), - OF_DEV_AUXDATA("nokia,n900-ir", 0, "n900-ir", &rx51_ir_data), /* Only on am3517 */ OF_DEV_AUXDATA("ti,davinci_mdio", 0x5c030000, "davinci_mdio.0", NULL), OF_DEV_AUXDATA("ti,am3517-emac", 0x5c000000, "davinci_emac.0", diff --git a/arch/arm/mach-omap2/pm-debug.c b/arch/arm/mach-omap2/pm-debug.c index 5c46ea6756d7..acb698d5780f 100644 --- a/arch/arm/mach-omap2/pm-debug.c +++ b/arch/arm/mach-omap2/pm-debug.c @@ -31,7 +31,6 @@ #include "clock.h" #include "powerdomain.h" #include "clockdomain.h" -#include "omap-pm.h" #include "soc.h" #include "cm2xxx_3xxx.h" @@ -240,10 +239,6 @@ static int option_set(void *data, u64 val) *option = val; if (option == &enable_off_mode) { - if (val) - omap_pm_enable_off_mode(); - else - omap_pm_disable_off_mode(); if (cpu_is_omap34xx()) omap3_pm_off_mode_enable(val); } diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 6f68576e5695..b98c46d7f112 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -16,11 +16,11 @@ #include #include #include +#include #include #include -#include "omap-pm.h" #include "omap_device.h" #include "common.h" @@ -230,14 +230,6 @@ static void __init omap4_init_voltages(void) omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva"); } -static int __init omap2_common_pm_init(void) -{ - omap_pm_if_init(); - - return 0; -} -omap_postcore_initcall(omap2_common_pm_init); - int __init omap2_common_pm_late_init(void) { /* Init the voltage layer */ diff --git a/arch/arm/mach-omap2/timer.c b/arch/arm/mach-omap2/timer.c index 4fb4dc24e5e9..61dd72df119c 100644 --- a/arch/arm/mach-omap2/timer.c +++ b/arch/arm/mach-omap2/timer.c @@ -50,7 +50,6 @@ #include "omap_device.h" #include #include -#include "omap-pm.h" #include "soc.h" #include "common.h" diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig index afc1a1d4f7a5..c0a242cae79a 100644 --- a/arch/arm/plat-omap/Kconfig +++ b/arch/arm/plat-omap/Kconfig @@ -115,16 +115,6 @@ config OMAP_SERIAL_WAKE to data on the serial RX line. This allows you to wake the system from serial console. -choice - prompt "OMAP PM layer selection" - depends on ARCH_OMAP - default OMAP_PM_NOOP - -config OMAP_PM_NOOP - bool "No-op/debug PM layer" - -endchoice - endmenu endif diff --git a/drivers/media/rc/ir-rx51.c b/drivers/media/rc/ir-rx51.c index 49265f02e772..8a93f7468622 100644 --- a/drivers/media/rc/ir-rx51.c +++ b/drivers/media/rc/ir-rx51.c @@ -22,7 +22,6 @@ #include #include -#include #define WBUF_LEN 256 @@ -31,7 +30,6 @@ struct ir_rx51 { struct pwm_device *pwm; struct hrtimer timer; struct device *dev; - struct ir_rx51_platform_data *pdata; wait_queue_head_t wqueue; unsigned int freq; /* carrier frequency */ @@ -130,10 +128,9 @@ static int ir_rx51_tx(struct rc_dev *dev, unsigned int *buffer, ir_rx51->wbuf[count] = -1; /* Insert termination mark */ /* - * Adjust latency requirements so the device doesn't go in too - * deep sleep states + * REVISIT: Adjust latency requirements so the device doesn't go in too + * deep sleep states with pm_qos_add_request(). */ - ir_rx51->pdata->set_max_mpu_wakeup_lat(ir_rx51->dev, 50); ir_rx51_on(ir_rx51); ir_rx51->wbuf_index = 1; @@ -146,8 +143,7 @@ static int ir_rx51_tx(struct rc_dev *dev, unsigned int *buffer, */ wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0); - /* We can sleep again */ - ir_rx51->pdata->set_max_mpu_wakeup_lat(ir_rx51->dev, -1); + /* REVISIT: Remove pm_qos constraint, we can sleep again */ return count; } @@ -244,13 +240,6 @@ static int ir_rx51_probe(struct platform_device *dev) struct pwm_device *pwm; struct rc_dev *rcdev; - ir_rx51.pdata = dev->dev.platform_data; - - if (!ir_rx51.pdata) { - dev_err(&dev->dev, "Platform Data is missing\n"); - return -ENXIO; - } - pwm = pwm_get(&dev->dev, NULL); if (IS_ERR(pwm)) { int err = PTR_ERR(pwm); diff --git a/include/linux/platform_data/media/ir-rx51.h b/include/linux/platform_data/media/ir-rx51.h deleted file mode 100644 index 9d127aa648e7..000000000000 --- a/include/linux/platform_data/media/ir-rx51.h +++ /dev/null @@ -1,9 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _IR_RX51_H -#define _IR_RX51_H - -struct ir_rx51_platform_data { - int(*set_max_mpu_wakeup_lat)(struct device *dev, long t); -}; - -#endif -- cgit From 1e6c06a7e88c251d8a30271ad5206fbd967a4576 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Sun, 8 Apr 2018 11:06:57 +0800 Subject: hwspinlock: Introduce one new mode for hwspinlock In some scenarios, user need do some time-consuming or sleepable operations under the hardware spinlock protection for synchronization between the multiple subsystems. For example, there is one PMIC efuse on Spreadtrum platform, which need to be accessed under one hardware lock. But during the hardware lock protection, the efuse operation is time-consuming to almost 5 ms, so we can not disable the interrupts or preemption so long in this case. Thus we can introduce one new mode to indicate that we just acquire the hardware lock and do not disable interrupts or preemption, meanwhile we should force user to protect the hardware lock with mutex or spinlock to avoid dead-lock. Signed-off-by: Baolin Wang Signed-off-by: Bjorn Andersson --- drivers/hwspinlock/hwspinlock_core.c | 34 ++++++++++++++++----- include/linux/hwspinlock.h | 58 ++++++++++++++++++++++++++++++++++++ 2 files changed, 85 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/drivers/hwspinlock/hwspinlock_core.c b/drivers/hwspinlock/hwspinlock_core.c index f4a59f5631e4..5278d0560a4a 100644 --- a/drivers/hwspinlock/hwspinlock_core.c +++ b/drivers/hwspinlock/hwspinlock_core.c @@ -71,10 +71,16 @@ static DEFINE_MUTEX(hwspinlock_tree_lock); * This function attempts to lock an hwspinlock, and will immediately * fail if the hwspinlock is already taken. * - * Upon a successful return from this function, preemption (and possibly - * interrupts) is disabled, so the caller must not sleep, and is advised to - * release the hwspinlock as soon as possible. This is required in order to - * minimize remote cores polling on the hardware interconnect. + * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine + * of getting hardware lock with mutex or spinlock. Since in some scenarios, + * user need some time-consuming or sleepable operations under the hardware + * lock, they need one sleepable lock (like mutex) to protect the operations. + * + * If the mode is not HWLOCK_RAW, upon a successful return from this function, + * preemption (and possibly interrupts) is disabled, so the caller must not + * sleep, and is advised to release the hwspinlock as soon as possible. This is + * required in order to minimize remote cores polling on the hardware + * interconnect. * * The user decides whether local interrupts are disabled or not, and if yes, * whether he wants their previous state to be saved. It is up to the user @@ -113,6 +119,9 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) case HWLOCK_IRQ: ret = spin_trylock_irq(&hwlock->lock); break; + case HWLOCK_RAW: + ret = 1; + break; default: ret = spin_trylock(&hwlock->lock); break; @@ -134,6 +143,9 @@ int __hwspin_trylock(struct hwspinlock *hwlock, int mode, unsigned long *flags) case HWLOCK_IRQ: spin_unlock_irq(&hwlock->lock); break; + case HWLOCK_RAW: + /* Nothing to do */ + break; default: spin_unlock(&hwlock->lock); break; @@ -170,9 +182,14 @@ EXPORT_SYMBOL_GPL(__hwspin_trylock); * is already taken, the function will busy loop waiting for it to * be released, but give up after @timeout msecs have elapsed. * - * Upon a successful return from this function, preemption is disabled - * (and possibly local interrupts, too), so the caller must not sleep, - * and is advised to release the hwspinlock as soon as possible. + * Caution: If the mode is HWLOCK_RAW, that means user must protect the routine + * of getting hardware lock with mutex or spinlock. Since in some scenarios, + * user need some time-consuming or sleepable operations under the hardware + * lock, they need one sleepable lock (like mutex) to protect the operations. + * + * If the mode is not HWLOCK_RAW, upon a successful return from this function, + * preemption is disabled (and possibly local interrupts, too), so the caller + * must not sleep, and is advised to release the hwspinlock as soon as possible. * This is required in order to minimize remote cores polling on the * hardware interconnect. * @@ -266,6 +283,9 @@ void __hwspin_unlock(struct hwspinlock *hwlock, int mode, unsigned long *flags) case HWLOCK_IRQ: spin_unlock_irq(&hwlock->lock); break; + case HWLOCK_RAW: + /* Nothing to do */ + break; default: spin_unlock(&hwlock->lock); break; diff --git a/include/linux/hwspinlock.h b/include/linux/hwspinlock.h index 859d673d98c8..fe450ee58d55 100644 --- a/include/linux/hwspinlock.h +++ b/include/linux/hwspinlock.h @@ -24,6 +24,7 @@ /* hwspinlock mode argument */ #define HWLOCK_IRQSTATE 0x01 /* Disable interrupts, save state */ #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ +#define HWLOCK_RAW 0x03 struct device; struct device_node; @@ -175,6 +176,25 @@ static inline int hwspin_trylock_irq(struct hwspinlock *hwlock) return __hwspin_trylock(hwlock, HWLOCK_IRQ, NULL); } +/** + * hwspin_trylock_raw() - attempt to lock a specific hwspinlock + * @hwlock: an hwspinlock which we want to trylock + * + * This function attempts to lock an hwspinlock, and will immediately fail + * if the hwspinlock is already taken. + * + * Caution: User must protect the routine of getting hardware lock with mutex + * or spinlock to avoid dead-lock, that will let user can do some time-consuming + * or sleepable operations under the hardware lock. + * + * Returns 0 if we successfully locked the hwspinlock, -EBUSY if + * the hwspinlock was already taken, and -EINVAL if @hwlock is invalid. + */ +static inline int hwspin_trylock_raw(struct hwspinlock *hwlock) +{ + return __hwspin_trylock(hwlock, HWLOCK_RAW, NULL); +} + /** * hwspin_trylock() - attempt to lock a specific hwspinlock * @hwlock: an hwspinlock which we want to trylock @@ -242,6 +262,29 @@ int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int to) return __hwspin_lock_timeout(hwlock, to, HWLOCK_IRQ, NULL); } +/** + * hwspin_lock_timeout_raw() - lock an hwspinlock with timeout limit + * @hwlock: the hwspinlock to be locked + * @to: timeout value in msecs + * + * This function locks the underlying @hwlock. If the @hwlock + * is already taken, the function will busy loop waiting for it to + * be released, but give up when @timeout msecs have elapsed. + * + * Caution: User must protect the routine of getting hardware lock with mutex + * or spinlock to avoid dead-lock, that will let user can do some time-consuming + * or sleepable operations under the hardware lock. + * + * Returns 0 when the @hwlock was successfully taken, and an appropriate + * error code otherwise (most notably an -ETIMEDOUT if the @hwlock is still + * busy after @timeout msecs). The function will never sleep. + */ +static inline +int hwspin_lock_timeout_raw(struct hwspinlock *hwlock, unsigned int to) +{ + return __hwspin_lock_timeout(hwlock, to, HWLOCK_RAW, NULL); +} + /** * hwspin_lock_timeout() - lock an hwspinlock with timeout limit * @hwlock: the hwspinlock to be locked @@ -301,6 +344,21 @@ static inline void hwspin_unlock_irq(struct hwspinlock *hwlock) __hwspin_unlock(hwlock, HWLOCK_IRQ, NULL); } +/** + * hwspin_unlock_raw() - unlock hwspinlock + * @hwlock: a previously-acquired hwspinlock which we want to unlock + * + * This function will unlock a specific hwspinlock. + * + * @hwlock must be already locked (e.g. by hwspin_trylock()) before calling + * this function: it is a bug to call unlock on a @hwlock that is already + * unlocked. + */ +static inline void hwspin_unlock_raw(struct hwspinlock *hwlock) +{ + __hwspin_unlock(hwlock, HWLOCK_RAW, NULL); +} + /** * hwspin_unlock() - unlock hwspinlock * @hwlock: a previously-acquired hwspinlock which we want to unlock -- cgit From fe1bd78bf18a7cb3eb76fceea9193534fb6619e3 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Mon, 2 Apr 2018 13:06:05 -0700 Subject: ARM: imx: Update spi_imx platform data to reflect current state The docs for the spi_imx platform data still refer to a -32 offset used to specify a native chip select. This was removed in commit 602c8f4485cd ("spi: imx: fix use of native chip-selects with devicetree") and no longer works as documented. Update documentation. The macro MXC_SPI_CS() is no longer is needed. If a board uses all native chip selects, then it's not necessary to specify a chip select array at all, as all native is the default (this is how device-tree configured SPI masters work too). Most of the spi-imx platform data users have their chip select arrays removed by this patch. This patch also fixes a bug in mx31moboard introduced in the '602 commit. When that board was updated in commit 901f26bce64a ("ARM: imx: set correct chip_select in platform setup") to reflect the SPI change, only SPI bus 2 was updated and SPI bus 1 was left with non-sequential chip selects. The mc13783 spi device on bus 1 had its chip select updated as if it were on bus 2. CC: Sascha Hauer CC: Fabio Estevam Acked-by: Greg Ungerer Reviewed-by: Oleksij Rempel Signed-off-by: Trent Piepho Signed-off-by: Shawn Guo --- arch/arm/mach-imx/mach-mx31_3ds.c | 18 ++---------------- arch/arm/mach-imx/mach-mx31lilly.c | 12 ++---------- arch/arm/mach-imx/mach-mx31lite.c | 16 ++-------------- arch/arm/mach-imx/mach-mx31moboard.c | 17 +++-------------- arch/arm/mach-imx/mach-pcm037_eet.c | 5 +---- include/linux/platform_data/spi-imx.h | 29 +++++++++++++++++------------ 6 files changed, 27 insertions(+), 70 deletions(-) (limited to 'include/linux') diff --git a/arch/arm/mach-imx/mach-mx31_3ds.c b/arch/arm/mach-imx/mach-mx31_3ds.c index 68c3f0799d5b..9d87f1dcf7bb 100644 --- a/arch/arm/mach-imx/mach-mx31_3ds.c +++ b/arch/arm/mach-imx/mach-mx31_3ds.c @@ -374,26 +374,12 @@ static struct imx_ssi_platform_data mx31_3ds_ssi_pdata = { }; /* SPI */ -static int spi0_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master spi0_pdata __initconst = { - .chipselect = spi0_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi0_internal_chipselect), -}; - -static int spi1_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), + .num_chipselect = 3, }; static const struct spi_imx_master spi1_pdata __initconst = { - .chipselect = spi1_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi1_internal_chipselect), + .num_chipselect = 3, }; static struct spi_board_info mx31_3ds_spi_devs[] __initdata = { diff --git a/arch/arm/mach-imx/mach-mx31lilly.c b/arch/arm/mach-imx/mach-mx31lilly.c index 6fd463642954..8bf52819d4d9 100644 --- a/arch/arm/mach-imx/mach-mx31lilly.c +++ b/arch/arm/mach-imx/mach-mx31lilly.c @@ -226,20 +226,12 @@ static void __init lilly1131_usb_init(void) /* SPI */ -static int spi_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master spi0_pdata __initconst = { - .chipselect = spi_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), + .num_chipselect = 3, }; static const struct spi_imx_master spi1_pdata __initconst = { - .chipselect = spi_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi_internal_chipselect), + .num_chipselect = 3, }; static struct mc13xxx_platform_data mc13783_pdata __initdata = { diff --git a/arch/arm/mach-imx/mach-mx31lite.c b/arch/arm/mach-imx/mach-mx31lite.c index a3250bc7f114..a3cbba6c955b 100644 --- a/arch/arm/mach-imx/mach-mx31lite.c +++ b/arch/arm/mach-imx/mach-mx31lite.c @@ -83,15 +83,8 @@ static const struct imxuart_platform_data uart_pdata __initconst = { }; /* SPI */ -static int spi0_internal_chipselect[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(1), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master spi0_pdata __initconst = { - .chipselect = spi0_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi0_internal_chipselect), + .num_chipselect = 3, }; static const struct mxc_nand_platform_data @@ -133,13 +126,8 @@ static struct platform_device smsc911x_device = { * The MC13783 is the only hard-wired SPI device on the module. */ -static int spi1_internal_chipselect[] = { - MXC_SPI_CS(0), -}; - static const struct spi_imx_master spi1_pdata __initconst = { - .chipselect = spi1_internal_chipselect, - .num_chipselect = ARRAY_SIZE(spi1_internal_chipselect), + .num_chipselect = 1, }; static struct mc13xxx_platform_data mc13783_pdata __initdata = { diff --git a/arch/arm/mach-imx/mach-mx31moboard.c b/arch/arm/mach-imx/mach-mx31moboard.c index 7716f83aecdd..643a3d749703 100644 --- a/arch/arm/mach-imx/mach-mx31moboard.c +++ b/arch/arm/mach-imx/mach-mx31moboard.c @@ -152,14 +152,8 @@ static const struct imxi2c_platform_data moboard_i2c1_data __initconst = { .bitrate = 100000, }; -static int moboard_spi1_cs[] = { - MXC_SPI_CS(0), - MXC_SPI_CS(2), -}; - static const struct spi_imx_master moboard_spi1_pdata __initconst = { - .chipselect = moboard_spi1_cs, - .num_chipselect = ARRAY_SIZE(moboard_spi1_cs), + .num_chipselect = 3, }; static struct regulator_consumer_supply sdhc_consumers[] = { @@ -296,19 +290,14 @@ static struct spi_board_info moboard_spi_board_info[] __initdata = { /* irq number is run-time assigned */ .max_speed_hz = 300000, .bus_num = 1, - .chip_select = 1, + .chip_select = 0, .platform_data = &moboard_pmic, .mode = SPI_CS_HIGH, }, }; -static int moboard_spi2_cs[] = { - MXC_SPI_CS(0), MXC_SPI_CS(1), -}; - static const struct spi_imx_master moboard_spi2_pdata __initconst = { - .chipselect = moboard_spi2_cs, - .num_chipselect = ARRAY_SIZE(moboard_spi2_cs), + .num_chipselect = 2, }; #define SDHC1_CD IOMUX_TO_GPIO(MX31_PIN_ATA_CS0) diff --git a/arch/arm/mach-imx/mach-pcm037_eet.c b/arch/arm/mach-imx/mach-pcm037_eet.c index 95bd97710494..15bc956d466b 100644 --- a/arch/arm/mach-imx/mach-pcm037_eet.c +++ b/arch/arm/mach-imx/mach-pcm037_eet.c @@ -56,11 +56,8 @@ static struct spi_board_info pcm037_spi_dev[] = { }; /* Platform Data for MXC CSPI */ -static int pcm037_spi1_cs[] = { MXC_SPI_CS(0), MXC_SPI_CS(1), }; - static const struct spi_imx_master pcm037_spi1_pdata __initconst = { - .chipselect = pcm037_spi1_cs, - .num_chipselect = ARRAY_SIZE(pcm037_spi1_cs), + .num_chipselect = 2, }; /* GPIO-keys input device */ diff --git a/include/linux/platform_data/spi-imx.h b/include/linux/platform_data/spi-imx.h index 6f012fefa1a2..328f670d10bd 100644 --- a/include/linux/platform_data/spi-imx.h +++ b/include/linux/platform_data/spi-imx.h @@ -5,24 +5,29 @@ /* * struct spi_imx_master - device.platform_data for SPI controller devices. - * @chipselect: Array of chipselects for this master. Numbers >= 0 mean gpio - * pins, numbers < 0 mean internal CSPI chipselects according - * to MXC_SPI_CS(). Normally you want to use gpio based chip - * selects as the CSPI module tries to be intelligent about - * when to assert the chipselect: The CSPI module deasserts the - * chipselect once it runs out of input data. The other problem - * is that it is not possible to mix between high active and low - * active chipselects on one single bus using the internal - * chipselects. Unfortunately Freescale decided to put some + * @chipselect: Array of chipselects for this master or NULL. Numbers >= 0 + * mean GPIO pins, -ENOENT means internal CSPI chipselect + * matching the position in the array. E.g., if chipselect[1] = + * -ENOENT then a SPI slave using chip select 1 will use the + * native SS1 line of the CSPI. Omitting the array will use + * all native chip selects. + + * Normally you want to use gpio based chip selects as the CSPI + * module tries to be intelligent about when to assert the + * chipselect: The CSPI module deasserts the chipselect once it + * runs out of input data. The other problem is that it is not + * possible to mix between high active and low active chipselects + * on one single bus using the internal chipselects. + * Unfortunately, on some SoCs, Freescale decided to put some * chipselects on dedicated pins which are not usable as gpios, * so we have to support the internal chipselects. - * @num_chipselect: ARRAY_SIZE(chipselect) + * + * @num_chipselect: If @chipselect is specified, ARRAY_SIZE(chipselect), + * otherwise the number of native chip selects. */ struct spi_imx_master { int *chipselect; int num_chipselect; }; -#define MXC_SPI_CS(no) ((no) - 32) - #endif /* __MACH_SPI_H_*/ -- cgit From b6240a4df0186c03e5ffff6f61570ed31a1a5172 Mon Sep 17 00:00:00 2001 From: Jason Yan Date: Mon, 26 Mar 2018 17:27:41 +0800 Subject: scsi: libsas: add transport class for ATA devices Now ata devices attached with sas controller do not have transport class, so that we can not see any information of these ata devices in /sys/class/ata_port(or ata_link or ata_device). Add transport class for the ata devices attached with sas controller. The /sys/class directory will show the infomation of the ata devices as follows: localhost:/sys/class # ls ata* ata_device: dev1.0 dev2.0 ata_link: link1 link2 ata_port: ata1 ata2 No functional change of the device scanning and io path. The ata transport class was deleted when destroying the sas devices. Signed-off-by: Jason Yan CC: Dan Williams CC: Tejun Heo Acked-by: Tejun Heo Signed-off-by: Martin K. Petersen --- drivers/ata/libata-scsi.c | 12 ++++++++++++ drivers/scsi/libsas/sas_ata.c | 5 +++++ drivers/scsi/libsas/sas_discover.c | 1 + include/linux/libata.h | 2 ++ 4 files changed, 20 insertions(+) (limited to 'include/linux') diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 89a9d4a2efc8..1c9f80fbc51c 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -5051,6 +5051,18 @@ int ata_sas_port_init(struct ata_port *ap) } EXPORT_SYMBOL_GPL(ata_sas_port_init); +int ata_sas_tport_add(struct device *parent, struct ata_port *ap) +{ + return ata_tport_add(parent, ap); +} +EXPORT_SYMBOL_GPL(ata_sas_tport_add); + +void ata_sas_tport_delete(struct ata_port *ap) +{ + ata_tport_delete(ap); +} +EXPORT_SYMBOL_GPL(ata_sas_tport_delete); + /** * ata_sas_port_destroy - Destroy a SATA port allocated by ata_sas_port_alloc * @ap: SATA port to destroy diff --git a/drivers/scsi/libsas/sas_ata.c b/drivers/scsi/libsas/sas_ata.c index 0cc1567eacc1..ff1d612f6fb9 100644 --- a/drivers/scsi/libsas/sas_ata.c +++ b/drivers/scsi/libsas/sas_ata.c @@ -577,6 +577,11 @@ int sas_ata_init(struct domain_device *found_dev) ata_sas_port_destroy(ap); return rc; } + rc = ata_sas_tport_add(found_dev->sata_dev.ata_host.dev, ap); + if (rc) { + ata_sas_port_destroy(ap); + return rc; + } found_dev->sata_dev.ap = ap; return 0; diff --git a/drivers/scsi/libsas/sas_discover.c b/drivers/scsi/libsas/sas_discover.c index a0fa7ef3a071..1ffca28fe6a8 100644 --- a/drivers/scsi/libsas/sas_discover.c +++ b/drivers/scsi/libsas/sas_discover.c @@ -314,6 +314,7 @@ void sas_free_device(struct kref *kref) kfree(dev->ex_dev.ex_phy); if (dev_is_sata(dev) && dev->sata_dev.ap) { + ata_sas_tport_delete(dev->sata_dev.ap); ata_sas_port_destroy(dev->sata_dev.ap); dev->sata_dev.ap = NULL; } diff --git a/include/linux/libata.h b/include/linux/libata.h index 1795fecdea17..0619ebf4d475 100644 --- a/include/linux/libata.h +++ b/include/linux/libata.h @@ -1130,6 +1130,8 @@ extern void ata_sas_async_probe(struct ata_port *ap); extern int ata_sas_sync_probe(struct ata_port *ap); extern int ata_sas_port_init(struct ata_port *); extern int ata_sas_port_start(struct ata_port *ap); +extern int ata_sas_tport_add(struct device *parent, struct ata_port *ap); +extern void ata_sas_tport_delete(struct ata_port *ap); extern void ata_sas_port_stop(struct ata_port *ap); extern int ata_sas_slave_configure(struct scsi_device *, struct ata_port *); extern int ata_sas_queuecmd(struct scsi_cmnd *cmd, struct ata_port *ap); -- cgit From 63273cb40101b6f303a5493f1bdf629d4ab3746b Mon Sep 17 00:00:00 2001 From: Long Li Date: Tue, 27 Mar 2018 17:48:38 -0700 Subject: scsi: vmbus: Add function to report available ring buffer to write in total ring size percentage Netvsc has a function to calculate how much ring buffer in percentage is available to write. This function is also useful for storvsc and other vmbus devices. Define a similar function in vmbus to be used by other vmbus devices. Signed-off-by: Long Li Acked-by: David S. Miller Signed-off-by: Martin K. Petersen --- drivers/hv/ring_buffer.c | 2 ++ include/linux/hyperv.h | 12 ++++++++++++ 2 files changed, 14 insertions(+) (limited to 'include/linux') diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 8699bb969e7e..3c836c099a8f 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -227,6 +227,8 @@ int hv_ringbuffer_init(struct hv_ring_buffer_info *ring_info, ring_info->ring_buffer->feature_bits.value = 1; ring_info->ring_size = page_cnt << PAGE_SHIFT; + ring_info->ring_size_div10_reciprocal = + reciprocal_value(ring_info->ring_size / 10); ring_info->ring_datasize = ring_info->ring_size - sizeof(struct hv_ring_buffer); diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 192ed8fbc403..9ac954ee577e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -35,6 +35,7 @@ #include #include #include +#include #define MAX_PAGE_BUFFER_COUNT 32 #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ @@ -120,6 +121,7 @@ struct hv_ring_buffer { struct hv_ring_buffer_info { struct hv_ring_buffer *ring_buffer; u32 ring_size; /* Include the shared header */ + struct reciprocal_value ring_size_div10_reciprocal; spinlock_t ring_lock; u32 ring_datasize; /* < ring_size */ @@ -154,6 +156,16 @@ static inline u32 hv_get_bytes_to_write(const struct hv_ring_buffer_info *rbi) return write; } +static inline u32 hv_get_avail_to_write_percent( + const struct hv_ring_buffer_info *rbi) +{ + u32 avail_write = hv_get_bytes_to_write(rbi); + + return reciprocal_divide( + (avail_write << 3) + (avail_write << 1), + rbi->ring_size_div10_reciprocal); +} + /* * VMBUS version is 32 bit entity broken up into * two 16 bit quantities: major_number. minor_number. -- cgit From a597043304a13defc646bb1f16514e4903b36c3c Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Tue, 10 Apr 2018 15:06:05 +0200 Subject: clk: Remove clk_init_cb typedef Since commit c08ee14cc6634457 ("clk: ti: change clock init to use generic of_clk_init"), there is only a single (private) user left of the (public) clk_init_cb typedef. Hence expand its single user in the core clock code, and remove the typedef. Signed-off-by: Geert Uytterhoeven Signed-off-by: Michael Turquette Link: lkml.kernel.org/r/1523365565-17124-1-git-send-email-geert+renesas@glider.be --- drivers/clk/clk.c | 2 +- include/linux/clk-provider.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ea67ac81c6f9..972f1ea4b63f 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -3906,7 +3906,7 @@ int of_clk_parent_fill(struct device_node *np, const char **parents, EXPORT_SYMBOL_GPL(of_clk_parent_fill); struct clock_provider { - of_clk_init_cb_t clk_init_cb; + void (*clk_init_cb)(struct device_node *); struct device_node *np; struct list_head node; }; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 210a890008f9..410a8627b8c0 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -802,8 +802,6 @@ unsigned long clk_hw_round_rate(struct clk_hw *hw, unsigned long rate); struct of_device_id; -typedef void (*of_clk_init_cb_t)(struct device_node *); - struct clk_onecell_data { struct clk **clks; unsigned int clk_num; -- cgit From 669ca0303ac93adba0e046d414165250861efdb7 Mon Sep 17 00:00:00 2001 From: ryang Date: Thu, 19 Apr 2018 12:18:50 -0400 Subject: regulator: tps6586x: Add support for TPS658624 This version is exists in the Samsung Galaxy Tab 10.1 which is based on the Nvidia Tegra 2 board. The TPS658624 has the same SM2 voltage table as TPS658623. Signed-off-by: ryang Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/tps6586x-regulator.c | 1 + include/linux/mfd/tps6586x.h | 1 + 2 files changed, 2 insertions(+) (limited to 'include/linux') diff --git a/drivers/regulator/tps6586x-regulator.c b/drivers/regulator/tps6586x-regulator.c index 9e9d22038017..ba3dae7b2d2d 100644 --- a/drivers/regulator/tps6586x-regulator.c +++ b/drivers/regulator/tps6586x-regulator.c @@ -342,6 +342,7 @@ static struct tps6586x_regulator *find_regulator_info(int id, int version) switch (version) { case TPS658623: + case TPS658624: table = tps658623_regulator; num = ARRAY_SIZE(tps658623_regulator); break; diff --git a/include/linux/mfd/tps6586x.h b/include/linux/mfd/tps6586x.h index 2fe68e481230..b19c2801a30e 100644 --- a/include/linux/mfd/tps6586x.h +++ b/include/linux/mfd/tps6586x.h @@ -18,6 +18,7 @@ #define TPS658621A 0x15 #define TPS658621CD 0x2c #define TPS658623 0x1b +#define TPS658624 0x0a #define TPS658640 0x01 #define TPS658640v2 0x02 #define TPS658643 0x03 -- cgit From 02f3703934a42417021405ef336fe45add13c3d1 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Wed, 18 Apr 2018 08:54:18 -0700 Subject: regulator: Don't return or expect -errno from of_map_mode() In of_get_regulation_constraints() we were taking the result of of_map_mode() (an unsigned int) and assigning it to an int. We were then checking whether this value was -EINVAL. Some implementers of of_map_mode() were returning -EINVAL (even though the return type of their function needed to be unsigned int) because they needed to signal an error back to of_get_regulation_constraints(). In general in the regulator framework the mode is always referred to as an unsigned int. While we could fix this to be a signed int (the highest value we store in there right now is 0x8), it's actually pretty clean to just define the regulator mode 0x0 (the lack of any bits set) as an invalid mode. Let's do that. Fixes: 5e5e3a42c653 ("regulator: of: Add support for parsing initial and suspend modes") Suggested-by: Javier Martinez Canillas Signed-off-by: Douglas Anderson Reviewed-by: Javier Martinez Canillas Signed-off-by: Mark Brown --- drivers/regulator/cpcap-regulator.c | 2 +- drivers/regulator/of_regulator.c | 13 +++++++------ drivers/regulator/twl-regulator.c | 2 +- include/linux/regulator/consumer.h | 1 + 4 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c index f541b80f1b54..bd910fe123d9 100644 --- a/drivers/regulator/cpcap-regulator.c +++ b/drivers/regulator/cpcap-regulator.c @@ -222,7 +222,7 @@ static unsigned int cpcap_map_mode(unsigned int mode) case CPCAP_BIT_AUDIO_LOW_PWR: return REGULATOR_MODE_STANDBY; default: - return -EINVAL; + return REGULATOR_MODE_INVALID; } } diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index f47264fa1940..0d3f73eacb99 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -31,6 +31,7 @@ static void of_get_regulation_constraints(struct device_node *np, struct regulation_constraints *constraints = &(*init_data)->constraints; struct regulator_state *suspend_state; struct device_node *suspend_np; + unsigned int mode; int ret, i; u32 pval; @@ -124,11 +125,11 @@ static void of_get_regulation_constraints(struct device_node *np, if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) { if (desc && desc->of_map_mode) { - ret = desc->of_map_mode(pval); - if (ret == -EINVAL) + mode = desc->of_map_mode(pval); + if (mode == REGULATOR_MODE_INVALID) pr_err("%s: invalid mode %u\n", np->name, pval); else - constraints->initial_mode = ret; + constraints->initial_mode = mode; } else { pr_warn("%s: mapping for mode %d not defined\n", np->name, pval); @@ -163,12 +164,12 @@ static void of_get_regulation_constraints(struct device_node *np, if (!of_property_read_u32(suspend_np, "regulator-mode", &pval)) { if (desc && desc->of_map_mode) { - ret = desc->of_map_mode(pval); - if (ret == -EINVAL) + mode = desc->of_map_mode(pval); + if (mode == REGULATOR_MODE_INVALID) pr_err("%s: invalid mode %u\n", np->name, pval); else - suspend_state->mode = ret; + suspend_state->mode = mode; } else { pr_warn("%s: mapping for mode %d not defined\n", np->name, pval); diff --git a/drivers/regulator/twl-regulator.c b/drivers/regulator/twl-regulator.c index a4456db5849d..884c7505ed91 100644 --- a/drivers/regulator/twl-regulator.c +++ b/drivers/regulator/twl-regulator.c @@ -274,7 +274,7 @@ static inline unsigned int twl4030reg_map_mode(unsigned int mode) case RES_STATE_SLEEP: return REGULATOR_MODE_STANDBY; default: - return -EINVAL; + return REGULATOR_MODE_INVALID; } } diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h index df176d7c2b87..25602afd4844 100644 --- a/include/linux/regulator/consumer.h +++ b/include/linux/regulator/consumer.h @@ -80,6 +80,7 @@ struct regmap; * These modes can be OR'ed together to make up a mask of valid register modes. */ +#define REGULATOR_MODE_INVALID 0x0 #define REGULATOR_MODE_FAST 0x1 #define REGULATOR_MODE_NORMAL 0x2 #define REGULATOR_MODE_IDLE 0x4 -- cgit From 8af70cd2ca508061088d5059ba8a8218aca7ddf1 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Fri, 20 Apr 2018 10:14:27 -0700 Subject: memory: aemif: add support for board files Currently aemif is supported in two places separately. By the platform driver in drivers/memory and by a hand crafted driver in mach-davinci. We want to drop the latter but also keep the legacy mode. Add support for board files to the aemif driver. The new structure in platform data currently only contains the chip select number, since currently existing users don't require anything else, but it can be extended in the future. While extending the platform data struct, add kernel docs describing its members. Signed-off-by: Bartosz Golaszewski Signed-off-by: Santosh Shilimkar --- drivers/memory/ti-aemif.c | 58 ++++++++++++++++++++++------------ include/linux/platform_data/ti-aemif.h | 25 +++++++++++++++ 2 files changed, 63 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/memory/ti-aemif.c b/drivers/memory/ti-aemif.c index 588e58d40d1b..31112f622b88 100644 --- a/drivers/memory/ti-aemif.c +++ b/drivers/memory/ti-aemif.c @@ -339,9 +339,6 @@ static int aemif_probe(struct platform_device *pdev) struct aemif_platform_data *pdata; struct of_dev_auxdata *dev_lookup; - if (np == NULL) - return 0; - aemif = devm_kzalloc(dev, sizeof(*aemif), GFP_KERNEL); if (!aemif) return -ENOMEM; @@ -363,8 +360,10 @@ static int aemif_probe(struct platform_device *pdev) aemif->clk_rate = clk_get_rate(aemif->clk) / MSEC_PER_SEC; - if (of_device_is_compatible(np, "ti,da850-aemif")) + if (np && of_device_is_compatible(np, "ti,da850-aemif")) aemif->cs_offset = 2; + else if (pdata) + aemif->cs_offset = pdata->cs_offset; res = platform_get_resource(pdev, IORESOURCE_MEM, 0); aemif->base = devm_ioremap_resource(dev, res); @@ -373,15 +372,23 @@ static int aemif_probe(struct platform_device *pdev) goto error; } - /* - * For every controller device node, there is a cs device node that - * describe the bus configuration parameters. This functions iterate - * over these nodes and update the cs data array. - */ - for_each_available_child_of_node(np, child_np) { - ret = of_aemif_parse_abus_config(pdev, child_np); - if (ret < 0) - goto error; + if (np) { + /* + * For every controller device node, there is a cs device node + * that describe the bus configuration parameters. This + * functions iterate over these nodes and update the cs data + * array. + */ + for_each_available_child_of_node(np, child_np) { + ret = of_aemif_parse_abus_config(pdev, child_np); + if (ret < 0) + goto error; + } + } else if (pdata && pdata->num_abus_data > 0) { + for (i = 0; i < pdata->num_abus_data; i++, aemif->num_cs++) { + aemif->cs_data[i].cs = pdata->abus_data[i].cs; + aemif_get_hw_params(pdev, i); + } } for (i = 0; i < aemif->num_cs; i++) { @@ -394,14 +401,25 @@ static int aemif_probe(struct platform_device *pdev) } /* - * Create a child devices explicitly from here to - * guarantee that the child will be probed after the AEMIF timing - * parameters are set. + * Create a child devices explicitly from here to guarantee that the + * child will be probed after the AEMIF timing parameters are set. */ - for_each_available_child_of_node(np, child_np) { - ret = of_platform_populate(child_np, NULL, dev_lookup, dev); - if (ret < 0) - goto error; + if (np) { + for_each_available_child_of_node(np, child_np) { + ret = of_platform_populate(child_np, NULL, + dev_lookup, dev); + if (ret < 0) + goto error; + } + } else { + for (i = 0; i < pdata->num_sub_devices; i++) { + pdata->sub_devices[i].dev.parent = dev; + ret = platform_device_register(&pdata->sub_devices[i]); + if (ret) { + dev_warn(dev, "Error register sub device %s\n", + pdata->sub_devices[i].name); + } + } } return 0; diff --git a/include/linux/platform_data/ti-aemif.h b/include/linux/platform_data/ti-aemif.h index ac72e115093c..e6407bafcbf8 100644 --- a/include/linux/platform_data/ti-aemif.h +++ b/include/linux/platform_data/ti-aemif.h @@ -16,8 +16,33 @@ #include +/** + * struct aemif_abus_data - Async bus configuration parameters. + * + * @cs - Chip-select number. + */ +struct aemif_abus_data { + u32 cs; +}; + +/** + * struct aemif_platform_data - Data to set up the TI aemif driver. + * + * @dev_lookup: of_dev_auxdata passed to of_platform_populate() for aemif + * subdevices. + * @cs_offset: Lowest allowed chip-select number. + * @abus_data: Array of async bus configuration entries. + * @num_abus_data: Number of abus entries. + * @sub_devices: Array of platform subdevices. + * @num_sub_devices: Number of subdevices. + */ struct aemif_platform_data { struct of_dev_auxdata *dev_lookup; + u32 cs_offset; + struct aemif_abus_data *abus_data; + size_t num_abus_data; + struct platform_device *sub_devices; + size_t num_sub_devices; }; #endif /* __TI_DAVINCI_AEMIF_DATA_H__ */ -- cgit From dbef91ec5482239055dd2db8ec656fc13d382add Mon Sep 17 00:00:00 2001 From: Martin Wilck Date: Wed, 18 Apr 2018 01:35:06 +0200 Subject: scsi: ilog2: create truly constant version for sparse Sparse emits errors about ilog2() in array indices because of the use of __ilog2_32() and __ilog2_64(), rightly so (https://www.spinics.net/lists/linux-sparse/msg03471.html). Create a const_ilog2() variant that works with sparse for this scenario. (Note: checkpatch.pl complains about missing parentheses, but that appears to be a false positive. I can get rid of the warning simply by inserting whitespace, making checkpatch "see" the whole macro). Signed-off-by: Martin Wilck Signed-off-by: Martin K. Petersen --- include/linux/log2.h | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/log2.h b/include/linux/log2.h index 41a1ae010993..2af7f77866d0 100644 --- a/include/linux/log2.h +++ b/include/linux/log2.h @@ -72,16 +72,13 @@ unsigned long __rounddown_pow_of_two(unsigned long n) } /** - * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value + * const_ilog2 - log base 2 of 32-bit or a 64-bit constant unsigned value * @n: parameter * - * constant-capable log of base 2 calculation - * - this can be used to initialise global variables from constant data, hence - * the massive ternary operator construction - * - * selects the appropriately-sized optimised version depending on sizeof(n) + * Use this where sparse expects a true constant expression, e.g. for array + * indices. */ -#define ilog2(n) \ +#define const_ilog2(n) \ ( \ __builtin_constant_p(n) ? ( \ (n) < 2 ? 0 : \ @@ -147,10 +144,26 @@ unsigned long __rounddown_pow_of_two(unsigned long n) (n) & (1ULL << 4) ? 4 : \ (n) & (1ULL << 3) ? 3 : \ (n) & (1ULL << 2) ? 2 : \ - 1 ) : \ - (sizeof(n) <= 4) ? \ - __ilog2_u32(n) : \ - __ilog2_u64(n) \ + 1) : \ + -1) + +/** + * ilog2 - log base 2 of 32-bit or a 64-bit unsigned value + * @n: parameter + * + * constant-capable log of base 2 calculation + * - this can be used to initialise global variables from constant data, hence + * the massive ternary operator construction + * + * selects the appropriately-sized optimised version depending on sizeof(n) + */ +#define ilog2(n) \ +( \ + __builtin_constant_p(n) ? \ + const_ilog2(n) : \ + (sizeof(n) <= 4) ? \ + __ilog2_u32(n) : \ + __ilog2_u64(n) \ ) /** -- cgit From f134fbbb4ff813dd227c9ce40b5c0b2078a77b07 Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Sat, 21 Apr 2018 08:54:40 +1000 Subject: mtd: spi-nor: clear Winbond Extended Address Reg on switch to 3-byte addressing. Winbond spi-nor flash 32MB and larger have an 'Extended Address Register' as one option for addressing beyond 16MB (Macronix has the same concept, Spansion has EXTADD bits in the Bank Address Register). According to section 8.2.7 Write Extended Address Register (C5h) of the Winbond W25Q256FV data sheet (256M-BIT SPI flash) The Extended Address Register is only effective when the device is in the 3-Byte Address Mode. When the device operates in the 4-Byte Address Mode (ADS=1), any command with address input of A31-A24 will replace the Extended Address Register values. It is recommended to check and update the Extended Address Register if necessary when the device is switched from 4-Byte to 3-Byte Address Mode. So the documentation suggests clearing the EAR after switching to 3-byte mode. Experimentation shows that the EAR is *always* one after the switch to 3-byte mode, so clearing the EAR is mandatory at shutdown for a subsequent 3-byte-addressed reboot to work. Note that some SOCs (e.g. MT7621) do not assert a reset line at normal reboot, so we cannot rely on hardware reset. The MT7621 does assert a reset line at watchdog-reset. Acked-by: Marek Vasut Signed-off-by: NeilBrown Signed-off-by: Boris Brezillon --- drivers/mtd/spi-nor/spi-nor.c | 14 ++++++++++++++ include/linux/mtd/spi-nor.h | 2 ++ 2 files changed, 16 insertions(+) (limited to 'include/linux') diff --git a/drivers/mtd/spi-nor/spi-nor.c b/drivers/mtd/spi-nor/spi-nor.c index 9363f299e4ee..494b7a269872 100644 --- a/drivers/mtd/spi-nor/spi-nor.c +++ b/drivers/mtd/spi-nor/spi-nor.c @@ -284,6 +284,20 @@ static inline int set_4byte(struct spi_nor *nor, const struct flash_info *info, if (need_wren) write_disable(nor); + if (!status && !enable && + JEDEC_MFR(info) == SNOR_MFR_WINBOND) { + /* + * On Winbond W25Q256FV, leaving 4byte mode causes + * the Extended Address Register to be set to 1, so all + * 3-byte-address reads come from the second 16M. + * We must clear the register to enable normal behavior. + */ + write_enable(nor); + nor->cmd_buf[0] = 0; + nor->write_reg(nor, SPINOR_OP_WREAR, nor->cmd_buf, 1); + write_disable(nor); + } + return status; default: /* Spansion style */ diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index de36969eb359..e60da0d34cc1 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -62,6 +62,8 @@ #define SPINOR_OP_RDCR 0x35 /* Read configuration register */ #define SPINOR_OP_RDFSR 0x70 /* Read flag status register */ #define SPINOR_OP_CLFSR 0x50 /* Clear flag status register */ +#define SPINOR_OP_RDEAR 0xc8 /* Read Extended Address Register */ +#define SPINOR_OP_WREAR 0xc5 /* Write Extended Address Register */ /* 4-byte address opcodes - used on Spansion and some Macronix flashes. */ #define SPINOR_OP_READ_4B 0x13 /* Read data bytes (low frequency) */ -- cgit From 05e6557b8ed833546ee2b66ce6b58fecf09f439e Mon Sep 17 00:00:00 2001 From: NeilBrown Date: Thu, 29 Mar 2018 15:26:48 +1100 Subject: staging: lustre: add container_of_safe() Luster has a container_of0() function which is similar to container_of() but passes an IS_ERR_OR_NULL() pointer through unchanged. This could be generally useful: bcache at last has a similar function. Naming is hard, but the precedent set by hlist_entry_safe() suggests a _safe suffix might be most consistent. So add container_of_safe() to kernel.h, and replace all occurrences of container_of0() with one of - list_first_entry, list_next_entry, when that is a better fit, - container_of(), when the pointer is used as a validpointer in surrounding code, - container_of_safe() when there is no obviously better alternative. Signed-off-by: NeilBrown Reviewed-by: James Simmons Signed-off-by: Greg Kroah-Hartman --- drivers/staging/lustre/include/linux/libcfs/libcfs.h | 11 ----------- drivers/staging/lustre/lustre/include/cl_object.h | 10 +++++----- drivers/staging/lustre/lustre/include/lu_object.h | 6 +++--- drivers/staging/lustre/lustre/llite/llite_nfs.c | 2 +- drivers/staging/lustre/lustre/llite/vvp_internal.h | 8 ++++---- drivers/staging/lustre/lustre/lmv/lmv_internal.h | 2 +- drivers/staging/lustre/lustre/lov/lov_cl_internal.h | 18 +++++++++--------- drivers/staging/lustre/lustre/lov/lov_internal.h | 2 +- drivers/staging/lustre/lustre/obdclass/lu_object.c | 8 ++++---- drivers/staging/lustre/lustre/obdecho/echo_client.c | 2 +- drivers/staging/lustre/lustre/osc/osc_cl_internal.h | 10 +++++----- drivers/staging/lustre/lustre/osc/osc_internal.h | 2 +- drivers/staging/lustre/lustre/osc/osc_io.c | 2 +- drivers/staging/lustre/lustre/osc/osc_object.c | 2 +- include/linux/kernel.h | 16 ++++++++++++++++ 15 files changed, 53 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/drivers/staging/lustre/include/linux/libcfs/libcfs.h b/drivers/staging/lustre/include/linux/libcfs/libcfs.h index 5d10aa32895b..6e7754b2f296 100644 --- a/drivers/staging/lustre/include/linux/libcfs/libcfs.h +++ b/drivers/staging/lustre/include/linux/libcfs/libcfs.h @@ -143,17 +143,6 @@ int libcfs_ioctl_getdata(struct libcfs_ioctl_hdr **hdr_pp, int libcfs_ioctl_data_adjust(struct libcfs_ioctl_data *data); int libcfs_ioctl(unsigned long cmd, void __user *arg); -/* container_of depends on "likely" which is defined in libcfs_private.h */ -static inline void *__container_of(void *ptr, unsigned long shift) -{ - if (IS_ERR_OR_NULL(ptr)) - return ptr; - return (char *)ptr - shift; -} - -#define container_of0(ptr, type, member) \ - ((type *)__container_of((void *)(ptr), offsetof(type, member))) - #define _LIBCFS_H extern struct miscdevice libcfs_dev; diff --git a/drivers/staging/lustre/lustre/include/cl_object.h b/drivers/staging/lustre/lustre/include/cl_object.h index 341a145c3331..6f7b991be809 100644 --- a/drivers/staging/lustre/lustre/include/cl_object.h +++ b/drivers/staging/lustre/lustre/include/cl_object.h @@ -1941,7 +1941,7 @@ static inline int lu_device_is_cl(const struct lu_device *d) static inline struct cl_device *lu2cl_dev(const struct lu_device *d) { LASSERT(!d || IS_ERR(d) || lu_device_is_cl(d)); - return container_of0(d, struct cl_device, cd_lu_dev); + return container_of_safe(d, struct cl_device, cd_lu_dev); } static inline struct lu_device *cl2lu_dev(struct cl_device *d) @@ -1952,13 +1952,13 @@ static inline struct lu_device *cl2lu_dev(struct cl_device *d) static inline struct cl_object *lu2cl(const struct lu_object *o) { LASSERT(!o || IS_ERR(o) || lu_device_is_cl(o->lo_dev)); - return container_of0(o, struct cl_object, co_lu); + return container_of_safe(o, struct cl_object, co_lu); } static inline const struct cl_object_conf * lu2cl_conf(const struct lu_object_conf *conf) { - return container_of0(conf, struct cl_object_conf, coc_lu); + return container_of_safe(conf, struct cl_object_conf, coc_lu); } static inline struct cl_object *cl_object_next(const struct cl_object *obj) @@ -1969,12 +1969,12 @@ static inline struct cl_object *cl_object_next(const struct cl_object *obj) static inline struct cl_device *cl_object_device(const struct cl_object *o) { LASSERT(!o || IS_ERR(o) || lu_device_is_cl(o->co_lu.lo_dev)); - return container_of0(o->co_lu.lo_dev, struct cl_device, cd_lu_dev); + return container_of_safe(o->co_lu.lo_dev, struct cl_device, cd_lu_dev); } static inline struct cl_object_header *luh2coh(const struct lu_object_header *h) { - return container_of0(h, struct cl_object_header, coh_lu); + return container_of_safe(h, struct cl_object_header, coh_lu); } static inline struct cl_site *cl_object_site(const struct cl_object *obj) diff --git a/drivers/staging/lustre/lustre/include/lu_object.h b/drivers/staging/lustre/lustre/include/lu_object.h index 35c7b582f36d..c3b0ed518819 100644 --- a/drivers/staging/lustre/lustre/include/lu_object.h +++ b/drivers/staging/lustre/lustre/include/lu_object.h @@ -745,15 +745,15 @@ struct lu_object *lu_object_find_slice(const struct lu_env *env, static inline struct lu_object *lu_object_top(struct lu_object_header *h) { LASSERT(!list_empty(&h->loh_layers)); - return container_of0(h->loh_layers.next, struct lu_object, lo_linkage); + return list_first_entry(&h->loh_layers, struct lu_object, lo_linkage); } /** * Next sub-object in the layering */ -static inline struct lu_object *lu_object_next(const struct lu_object *o) +static inline const struct lu_object *lu_object_next(const struct lu_object *o) { - return container_of0(o->lo_linkage.next, struct lu_object, lo_linkage); + return list_next_entry(o, lo_linkage); } /** diff --git a/drivers/staging/lustre/lustre/llite/llite_nfs.c b/drivers/staging/lustre/lustre/llite/llite_nfs.c index a6a1d80c711a..14172688d55f 100644 --- a/drivers/staging/lustre/lustre/llite/llite_nfs.c +++ b/drivers/staging/lustre/lustre/llite/llite_nfs.c @@ -223,7 +223,7 @@ static int ll_nfs_get_name_filldir(struct dir_context *ctx, const char *name, /* It is hack to access lde_fid for comparison with lgd_fid. * So the input 'name' must be part of the 'lu_dirent'. */ - struct lu_dirent *lde = container_of0(name, struct lu_dirent, lde_name); + struct lu_dirent *lde = container_of((void*)name, struct lu_dirent, lde_name); struct ll_getname_data *lgd = container_of(ctx, struct ll_getname_data, ctx); struct lu_fid fid; diff --git a/drivers/staging/lustre/lustre/llite/vvp_internal.h b/drivers/staging/lustre/lustre/llite/vvp_internal.h index 02ea5161d635..7d3abb43584a 100644 --- a/drivers/staging/lustre/lustre/llite/vvp_internal.h +++ b/drivers/staging/lustre/lustre/llite/vvp_internal.h @@ -263,22 +263,22 @@ static inline struct lu_device *vvp2lu_dev(struct vvp_device *vdv) static inline struct vvp_device *lu2vvp_dev(const struct lu_device *d) { - return container_of0(d, struct vvp_device, vdv_cl.cd_lu_dev); + return container_of_safe(d, struct vvp_device, vdv_cl.cd_lu_dev); } static inline struct vvp_device *cl2vvp_dev(const struct cl_device *d) { - return container_of0(d, struct vvp_device, vdv_cl); + return container_of_safe(d, struct vvp_device, vdv_cl); } static inline struct vvp_object *cl2vvp(const struct cl_object *obj) { - return container_of0(obj, struct vvp_object, vob_cl); + return container_of_safe(obj, struct vvp_object, vob_cl); } static inline struct vvp_object *lu2vvp(const struct lu_object *obj) { - return container_of0(obj, struct vvp_object, vob_cl.co_lu); + return container_of_safe(obj, struct vvp_object, vob_cl.co_lu); } static inline struct inode *vvp_object_inode(const struct cl_object *obj) diff --git a/drivers/staging/lustre/lustre/lmv/lmv_internal.h b/drivers/staging/lustre/lustre/lmv/lmv_internal.h index c27c3c32188d..68a99170c424 100644 --- a/drivers/staging/lustre/lustre/lmv/lmv_internal.h +++ b/drivers/staging/lustre/lustre/lmv/lmv_internal.h @@ -60,7 +60,7 @@ int lmv_revalidate_slaves(struct obd_export *exp, static inline struct obd_device *lmv2obd_dev(struct lmv_obd *lmv) { - return container_of0(lmv, struct obd_device, u.lmv); + return container_of_safe(lmv, struct obd_device, u.lmv); } static inline struct lmv_tgt_desc * diff --git a/drivers/staging/lustre/lustre/lov/lov_cl_internal.h b/drivers/staging/lustre/lustre/lov/lov_cl_internal.h index 1185eceaf497..2e9c75ebdda5 100644 --- a/drivers/staging/lustre/lustre/lov/lov_cl_internal.h +++ b/drivers/staging/lustre/lustre/lov/lov_cl_internal.h @@ -496,7 +496,7 @@ static inline struct lu_device *lov2lu_dev(struct lov_device *lov) static inline struct lov_device *lu2lov_dev(const struct lu_device *d) { LINVRNT(d->ld_type == &lov_device_type); - return container_of0(d, struct lov_device, ld_cl.cd_lu_dev); + return container_of(d, struct lov_device, ld_cl.cd_lu_dev); } static inline struct cl_device *lovsub2cl_dev(struct lovsub_device *lovsub) @@ -512,13 +512,13 @@ static inline struct lu_device *lovsub2lu_dev(struct lovsub_device *lovsub) static inline struct lovsub_device *lu2lovsub_dev(const struct lu_device *d) { LINVRNT(d->ld_type == &lovsub_device_type); - return container_of0(d, struct lovsub_device, acid_cl.cd_lu_dev); + return container_of(d, struct lovsub_device, acid_cl.cd_lu_dev); } static inline struct lovsub_device *cl2lovsub_dev(const struct cl_device *d) { LINVRNT(d->cd_lu_dev.ld_type == &lovsub_device_type); - return container_of0(d, struct lovsub_device, acid_cl); + return container_of(d, struct lovsub_device, acid_cl); } static inline struct lu_object *lov2lu(struct lov_object *lov) @@ -534,13 +534,13 @@ static inline struct cl_object *lov2cl(struct lov_object *lov) static inline struct lov_object *lu2lov(const struct lu_object *obj) { LINVRNT(lov_is_object(obj)); - return container_of0(obj, struct lov_object, lo_cl.co_lu); + return container_of(obj, struct lov_object, lo_cl.co_lu); } static inline struct lov_object *cl2lov(const struct cl_object *obj) { LINVRNT(lov_is_object(&obj->co_lu)); - return container_of0(obj, struct lov_object, lo_cl); + return container_of(obj, struct lov_object, lo_cl); } static inline struct lu_object *lovsub2lu(struct lovsub_object *los) @@ -556,13 +556,13 @@ static inline struct cl_object *lovsub2cl(struct lovsub_object *los) static inline struct lovsub_object *cl2lovsub(const struct cl_object *obj) { LINVRNT(lovsub_is_object(&obj->co_lu)); - return container_of0(obj, struct lovsub_object, lso_cl); + return container_of(obj, struct lovsub_object, lso_cl); } static inline struct lovsub_object *lu2lovsub(const struct lu_object *obj) { LINVRNT(lovsub_is_object(obj)); - return container_of0(obj, struct lovsub_object, lso_cl.co_lu); + return container_of(obj, struct lovsub_object, lso_cl.co_lu); } static inline struct lovsub_lock * @@ -590,14 +590,14 @@ static inline struct lov_lock *cl2lov_lock(const struct cl_lock_slice *slice) static inline struct lov_page *cl2lov_page(const struct cl_page_slice *slice) { LINVRNT(lov_is_object(&slice->cpl_obj->co_lu)); - return container_of0(slice, struct lov_page, lps_cl); + return container_of(slice, struct lov_page, lps_cl); } static inline struct lovsub_page * cl2lovsub_page(const struct cl_page_slice *slice) { LINVRNT(lovsub_is_object(&slice->cpl_obj->co_lu)); - return container_of0(slice, struct lovsub_page, lsb_cl); + return container_of(slice, struct lovsub_page, lsb_cl); } static inline struct lov_io *cl2lov_io(const struct lu_env *env, diff --git a/drivers/staging/lustre/lustre/lov/lov_internal.h b/drivers/staging/lustre/lustre/lov/lov_internal.h index 8dde3828f935..47042f27ca90 100644 --- a/drivers/staging/lustre/lustre/lov/lov_internal.h +++ b/drivers/staging/lustre/lustre/lov/lov_internal.h @@ -280,7 +280,7 @@ static inline bool lov_oinfo_is_dummy(const struct lov_oinfo *loi) static inline struct obd_device *lov2obd(const struct lov_obd *lov) { - return container_of0(lov, struct obd_device, u.lov); + return container_of_safe(lov, struct obd_device, u.lov); } #endif diff --git a/drivers/staging/lustre/lustre/obdclass/lu_object.c b/drivers/staging/lustre/lustre/obdclass/lu_object.c index 3ae16e8501c2..3de7dc0497c4 100644 --- a/drivers/staging/lustre/lustre/obdclass/lu_object.c +++ b/drivers/staging/lustre/lustre/obdclass/lu_object.c @@ -319,7 +319,7 @@ static void lu_object_free(const struct lu_env *env, struct lu_object *o) * lives as long as possible and ->loo_object_free() methods * can look at its contents. */ - o = container_of0(splice.prev, struct lu_object, lo_linkage); + o = container_of(splice.prev, struct lu_object, lo_linkage); list_del_init(&o->lo_linkage); o->lo_ops->loo_object_free(env, o); } @@ -404,8 +404,8 @@ int lu_site_purge_objects(const struct lu_env *env, struct lu_site *s, * races due to the reasons described in lu_object_put(). */ while (!list_empty(&dispose)) { - h = container_of0(dispose.next, - struct lu_object_header, loh_lru); + h = container_of(dispose.next, + struct lu_object_header, loh_lru); list_del_init(&h->loh_lru); lu_object_free(env, lu_object_top(h)); lprocfs_counter_incr(s->ls_stats, LU_SS_LRU_PURGED); @@ -579,7 +579,7 @@ static struct lu_object *htable_lookup(struct lu_site *s, return ERR_PTR(-ENOENT); } - h = container_of0(hnode, struct lu_object_header, loh_hash); + h = container_of(hnode, struct lu_object_header, loh_hash); if (likely(!lu_object_is_dying(h))) { cfs_hash_get(s->ls_obj_hash, hnode); lprocfs_counter_incr(s->ls_stats, LU_SS_CACHE_HIT); diff --git a/drivers/staging/lustre/lustre/obdecho/echo_client.c b/drivers/staging/lustre/lustre/obdecho/echo_client.c index 99a76db51ae0..767067b61109 100644 --- a/drivers/staging/lustre/lustre/obdecho/echo_client.c +++ b/drivers/staging/lustre/lustre/obdecho/echo_client.c @@ -99,7 +99,7 @@ static int echo_client_cleanup(struct obd_device *obddev); */ static inline struct echo_device *cl2echo_dev(const struct cl_device *dev) { - return container_of0(dev, struct echo_device, ed_cl); + return container_of_safe(dev, struct echo_device, ed_cl); } static inline struct cl_device *echo_dev2cl(struct echo_device *d) diff --git a/drivers/staging/lustre/lustre/osc/osc_cl_internal.h b/drivers/staging/lustre/lustre/osc/osc_cl_internal.h index 1449013722f6..dc25dd12d7d5 100644 --- a/drivers/staging/lustre/lustre/osc/osc_cl_internal.h +++ b/drivers/staging/lustre/lustre/osc/osc_cl_internal.h @@ -460,7 +460,7 @@ static inline int osc_is_object(const struct lu_object *obj) static inline struct osc_device *lu2osc_dev(const struct lu_device *d) { LINVRNT(d->ld_type == &osc_device_type); - return container_of0(d, struct osc_device, od_cl.cd_lu_dev); + return container_of(d, struct osc_device, od_cl.cd_lu_dev); } static inline struct obd_export *osc_export(const struct osc_object *obj) @@ -476,7 +476,7 @@ static inline struct client_obd *osc_cli(const struct osc_object *obj) static inline struct osc_object *cl2osc(const struct cl_object *obj) { LINVRNT(osc_is_object(&obj->co_lu)); - return container_of0(obj, struct osc_object, oo_cl); + return container_of(obj, struct osc_object, oo_cl); } static inline struct cl_object *osc2cl(const struct osc_object *obj) @@ -509,12 +509,12 @@ static inline enum cl_lock_mode osc_ldlm2cl_lock(enum ldlm_mode mode) static inline struct osc_page *cl2osc_page(const struct cl_page_slice *slice) { LINVRNT(osc_is_object(&slice->cpl_obj->co_lu)); - return container_of0(slice, struct osc_page, ops_cl); + return container_of(slice, struct osc_page, ops_cl); } static inline struct osc_page *oap2osc(struct osc_async_page *oap) { - return container_of0(oap, struct osc_page, ops_oap); + return container_of_safe(oap, struct osc_page, ops_oap); } static inline pgoff_t osc_index(struct osc_page *opg) @@ -545,7 +545,7 @@ osc_cl_page_osc(struct cl_page *page, struct osc_object *osc) static inline struct osc_lock *cl2osc_lock(const struct cl_lock_slice *slice) { LINVRNT(osc_is_object(&slice->cls_obj->co_lu)); - return container_of0(slice, struct osc_lock, ols_cl); + return container_of(slice, struct osc_lock, ols_cl); } static inline struct osc_lock *osc_lock_at(const struct cl_lock *lock) diff --git a/drivers/staging/lustre/lustre/osc/osc_internal.h b/drivers/staging/lustre/lustre/osc/osc_internal.h index 1c8ba4ad6e7f..fca020568c19 100644 --- a/drivers/staging/lustre/lustre/osc/osc_internal.h +++ b/drivers/staging/lustre/lustre/osc/osc_internal.h @@ -180,7 +180,7 @@ struct osc_device { static inline struct osc_device *obd2osc_dev(const struct obd_device *d) { - return container_of0(d->obd_lu_dev, struct osc_device, od_cl.cd_lu_dev); + return container_of_safe(d->obd_lu_dev, struct osc_device, od_cl.cd_lu_dev); } extern struct lu_kmem_descr osc_caches[]; diff --git a/drivers/staging/lustre/lustre/osc/osc_io.c b/drivers/staging/lustre/lustre/osc/osc_io.c index 76743faf3e6d..67734a8ed331 100644 --- a/drivers/staging/lustre/lustre/osc/osc_io.c +++ b/drivers/staging/lustre/lustre/osc/osc_io.c @@ -55,7 +55,7 @@ static struct osc_io *cl2osc_io(const struct lu_env *env, const struct cl_io_slice *slice) { - struct osc_io *oio = container_of0(slice, struct osc_io, oi_cl); + struct osc_io *oio = container_of_safe(slice, struct osc_io, oi_cl); LINVRNT(oio == osc_env_io(env)); return oio; diff --git a/drivers/staging/lustre/lustre/osc/osc_object.c b/drivers/staging/lustre/lustre/osc/osc_object.c index 4f81dd16f4f5..84240181c7ea 100644 --- a/drivers/staging/lustre/lustre/osc/osc_object.c +++ b/drivers/staging/lustre/lustre/osc/osc_object.c @@ -58,7 +58,7 @@ static struct lu_object *osc2lu(struct osc_object *osc) static struct osc_object *lu2osc(const struct lu_object *obj) { LINVRNT(osc_is_object(obj)); - return container_of0(obj, struct osc_object, oo_cl.co_lu); + return container_of(obj, struct osc_object, oo_cl.co_lu); } /***************************************************************************** diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 6a1eb0b0aad9..58d6645b1425 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -964,6 +964,22 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } "pointer type mismatch in container_of()"); \ ((type *)(__mptr - offsetof(type, member))); }) +/** + * container_of_safe - cast a member of a structure out to the containing structure + * @ptr: the pointer to the member. + * @type: the type of the container struct this is embedded in. + * @member: the name of the member within the struct. + * + * If IS_ERR_OR_NULL(ptr), ptr is returned unchanged. + */ +#define container_of_safe(ptr, type, member) ({ \ + void *__mptr = (void *)(ptr); \ + BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \ + !__same_type(*(ptr), void), \ + "pointer type mismatch in container_of()"); \ + IS_ERR_OR_NULL(ptr) ? ERR_CAST(ptr) : \ + ((type *)(__mptr - offsetof(type, member))); }) + /* Rebuild everything on CONFIG_FTRACE_MCOUNT_RECORD */ #ifdef CONFIG_FTRACE_MCOUNT_RECORD # define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD -- cgit From 2ff0dab80a8999e99a93fd70f8d701ec3deab207 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 18 Apr 2018 15:18:02 +0200 Subject: mfd: bd9571mwv: Add DDR Backup Power register bit definitions Add definitions for the KEEPON_* bits in the "BKUP Mode Cnt" register, which control the DDR rails to be kept powered when backup mode is enabled. Signed-off-by: Geert Uytterhoeven Acked-by: Lee Jones Signed-off-by: Mark Brown --- include/linux/mfd/bd9571mwv.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mfd/bd9571mwv.h b/include/linux/mfd/bd9571mwv.h index f0708ba4cbba..eb05569f752b 100644 --- a/include/linux/mfd/bd9571mwv.h +++ b/include/linux/mfd/bd9571mwv.h @@ -33,6 +33,11 @@ #define BD9571MWV_I2C_MD2_E1_BIT_2 0x12 #define BD9571MWV_BKUP_MODE_CNT 0x20 +#define BD9571MWV_BKUP_MODE_CNT_KEEPON_MASK GENMASK(3, 0) +#define BD9571MWV_BKUP_MODE_CNT_KEEPON_DDR0 BIT(0) +#define BD9571MWV_BKUP_MODE_CNT_KEEPON_DDR1 BIT(1) +#define BD9571MWV_BKUP_MODE_CNT_KEEPON_DDR0C BIT(2) +#define BD9571MWV_BKUP_MODE_CNT_KEEPON_DDR1C BIT(3) #define BD9571MWV_BKUP_MODE_STATUS 0x21 #define BD9571MWV_BKUP_RECOVERY_CNT 0x22 #define BD9571MWV_BKUP_CTRL_TIM_CNT 0x23 -- cgit From eddac5af06546d2e7a0730e3dc02dde3dc91098a Mon Sep 17 00:00:00 2001 From: Karthikeyan Ramasubramanian Date: Fri, 30 Mar 2018 11:08:17 -0600 Subject: soc: qcom: Add GENI based QUP Wrapper driver This driver manages the Generic Interface (GENI) firmware based Qualcomm Universal Peripheral (QUP) Wrapper. GENI based QUP is the next generation programmable module composed of multiple Serial Engines (SE) and supports a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. This driver also enables managing the serial interface independent aspects of Serial Engines. Signed-off-by: Karthikeyan Ramasubramanian Signed-off-by: Sagar Dharia Signed-off-by: Girish Mahadevan Reviewed-by: Evan Green Signed-off-by: Andy Gross --- drivers/soc/qcom/Kconfig | 9 + drivers/soc/qcom/Makefile | 1 + drivers/soc/qcom/qcom-geni-se.c | 748 ++++++++++++++++++++++++++++++++++++++++ include/linux/qcom-geni-se.h | 425 +++++++++++++++++++++++ 4 files changed, 1183 insertions(+) create mode 100644 drivers/soc/qcom/qcom-geni-se.c create mode 100644 include/linux/qcom-geni-se.h (limited to 'include/linux') diff --git a/drivers/soc/qcom/Kconfig b/drivers/soc/qcom/Kconfig index 5c4535b545cc..d0372de29834 100644 --- a/drivers/soc/qcom/Kconfig +++ b/drivers/soc/qcom/Kconfig @@ -3,6 +3,15 @@ # menu "Qualcomm SoC drivers" +config QCOM_GENI_SE + tristate "QCOM GENI Serial Engine Driver" + depends on ARCH_QCOM || COMPILE_TEST + help + This driver is used to manage Generic Interface (GENI) firmware based + Qualcomm Technologies, Inc. Universal Peripheral (QUP) Wrapper. This + driver is also used to manage the common aspects of multiple Serial + Engines present in the QUP. + config QCOM_GLINK_SSR tristate "Qualcomm Glink SSR driver" depends on RPMSG diff --git a/drivers/soc/qcom/Makefile b/drivers/soc/qcom/Makefile index dcebf2814e6d..959aa748f5ac 100644 --- a/drivers/soc/qcom/Makefile +++ b/drivers/soc/qcom/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_QCOM_GENI_SE) += qcom-geni-se.o obj-$(CONFIG_QCOM_GLINK_SSR) += glink_ssr.o obj-$(CONFIG_QCOM_GSBI) += qcom_gsbi.o obj-$(CONFIG_QCOM_MDT_LOADER) += mdt_loader.o diff --git a/drivers/soc/qcom/qcom-geni-se.c b/drivers/soc/qcom/qcom-geni-se.c new file mode 100644 index 000000000000..feed3db21c10 --- /dev/null +++ b/drivers/soc/qcom/qcom-geni-se.c @@ -0,0 +1,748 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * DOC: Overview + * + * Generic Interface (GENI) Serial Engine (SE) Wrapper driver is introduced + * to manage GENI firmware based Qualcomm Universal Peripheral (QUP) Wrapper + * controller. QUP Wrapper is designed to support various serial bus protocols + * like UART, SPI, I2C, I3C, etc. + */ + +/** + * DOC: Hardware description + * + * GENI based QUP is a highly-flexible and programmable module for supporting + * a wide range of serial interfaces like UART, SPI, I2C, I3C, etc. A single + * QUP module can provide upto 8 serial interfaces, using its internal + * serial engines. The actual configuration is determined by the target + * platform configuration. The protocol supported by each interface is + * determined by the firmware loaded to the serial engine. Each SE consists + * of a DMA Engine and GENI sub modules which enable serial engines to + * support FIFO and DMA modes of operation. + * + * + * +-----------------------------------------+ + * |QUP Wrapper | + * | +----------------------------+ | + * --QUP & SE Clocks--> | Serial Engine N | +-IO------> + * | | ... | | Interface + * <---Clock Perf.----+ +----+-----------------------+ | | + * State Interface | | Serial Engine 1 | | | + * | | | | | + * | | | | | + * <--------AHB-------> | | | | + * | | +----+ | + * | | | | + * | | | | + * <------SE IRQ------+ +----------------------------+ | + * | | + * +-----------------------------------------+ + * + * Figure 1: GENI based QUP Wrapper + * + * The GENI submodules include primary and secondary sequencers which are + * used to drive TX & RX operations. On serial interfaces that operate using + * master-slave model, primary sequencer drives both TX & RX operations. On + * serial interfaces that operate using peer-to-peer model, primary sequencer + * drives TX operation and secondary sequencer drives RX operation. + */ + +/** + * DOC: Software description + * + * GENI SE Wrapper driver is structured into 2 parts: + * + * geni_wrapper represents QUP Wrapper controller. This part of the driver + * manages QUP Wrapper information such as hardware version, clock + * performance table that is common to all the internal serial engines. + * + * geni_se represents serial engine. This part of the driver manages serial + * engine information such as clocks, containing QUP Wrapper, etc. This part + * of driver also supports operations (eg. initialize the concerned serial + * engine, select between FIFO and DMA mode of operation etc.) that are + * common to all the serial engines and are independent of serial interfaces. + */ + +#define MAX_CLK_PERF_LEVEL 32 +#define NUM_AHB_CLKS 2 + +/** + * @struct geni_wrapper - Data structure to represent the QUP Wrapper Core + * @dev: Device pointer of the QUP wrapper core + * @base: Base address of this instance of QUP wrapper core + * @ahb_clks: Handle to the primary & secondary AHB clocks + */ +struct geni_wrapper { + struct device *dev; + void __iomem *base; + struct clk_bulk_data ahb_clks[NUM_AHB_CLKS]; +}; + +#define QUP_HW_VER_REG 0x4 + +/* Common SE registers */ +#define GENI_INIT_CFG_REVISION 0x0 +#define GENI_S_INIT_CFG_REVISION 0x4 +#define GENI_OUTPUT_CTRL 0x24 +#define GENI_CGC_CTRL 0x28 +#define GENI_CLK_CTRL_RO 0x60 +#define GENI_IF_DISABLE_RO 0x64 +#define GENI_FW_S_REVISION_RO 0x6c +#define SE_GENI_BYTE_GRAN 0x254 +#define SE_GENI_TX_PACKING_CFG0 0x260 +#define SE_GENI_TX_PACKING_CFG1 0x264 +#define SE_GENI_RX_PACKING_CFG0 0x284 +#define SE_GENI_RX_PACKING_CFG1 0x288 +#define SE_GENI_M_GP_LENGTH 0x910 +#define SE_GENI_S_GP_LENGTH 0x914 +#define SE_DMA_TX_PTR_L 0xc30 +#define SE_DMA_TX_PTR_H 0xc34 +#define SE_DMA_TX_ATTR 0xc38 +#define SE_DMA_TX_LEN 0xc3c +#define SE_DMA_TX_IRQ_EN 0xc48 +#define SE_DMA_TX_IRQ_EN_SET 0xc4c +#define SE_DMA_TX_IRQ_EN_CLR 0xc50 +#define SE_DMA_TX_LEN_IN 0xc54 +#define SE_DMA_TX_MAX_BURST 0xc5c +#define SE_DMA_RX_PTR_L 0xd30 +#define SE_DMA_RX_PTR_H 0xd34 +#define SE_DMA_RX_ATTR 0xd38 +#define SE_DMA_RX_LEN 0xd3c +#define SE_DMA_RX_IRQ_EN 0xd48 +#define SE_DMA_RX_IRQ_EN_SET 0xd4c +#define SE_DMA_RX_IRQ_EN_CLR 0xd50 +#define SE_DMA_RX_LEN_IN 0xd54 +#define SE_DMA_RX_MAX_BURST 0xd5c +#define SE_DMA_RX_FLUSH 0xd60 +#define SE_GSI_EVENT_EN 0xe18 +#define SE_IRQ_EN 0xe1c +#define SE_DMA_GENERAL_CFG 0xe30 + +/* GENI_OUTPUT_CTRL fields */ +#define DEFAULT_IO_OUTPUT_CTRL_MSK GENMASK(6, 0) + +/* GENI_CGC_CTRL fields */ +#define CFG_AHB_CLK_CGC_ON BIT(0) +#define CFG_AHB_WR_ACLK_CGC_ON BIT(1) +#define DATA_AHB_CLK_CGC_ON BIT(2) +#define SCLK_CGC_ON BIT(3) +#define TX_CLK_CGC_ON BIT(4) +#define RX_CLK_CGC_ON BIT(5) +#define EXT_CLK_CGC_ON BIT(6) +#define PROG_RAM_HCLK_OFF BIT(8) +#define PROG_RAM_SCLK_OFF BIT(9) +#define DEFAULT_CGC_EN GENMASK(6, 0) + +/* SE_GSI_EVENT_EN fields */ +#define DMA_RX_EVENT_EN BIT(0) +#define DMA_TX_EVENT_EN BIT(1) +#define GENI_M_EVENT_EN BIT(2) +#define GENI_S_EVENT_EN BIT(3) + +/* SE_IRQ_EN fields */ +#define DMA_RX_IRQ_EN BIT(0) +#define DMA_TX_IRQ_EN BIT(1) +#define GENI_M_IRQ_EN BIT(2) +#define GENI_S_IRQ_EN BIT(3) + +/* SE_DMA_GENERAL_CFG */ +#define DMA_RX_CLK_CGC_ON BIT(0) +#define DMA_TX_CLK_CGC_ON BIT(1) +#define DMA_AHB_SLV_CFG_ON BIT(2) +#define AHB_SEC_SLV_CLK_CGC_ON BIT(3) +#define DUMMY_RX_NON_BUFFERABLE BIT(4) +#define RX_DMA_ZERO_PADDING_EN BIT(5) +#define RX_DMA_IRQ_DELAY_MSK GENMASK(8, 6) +#define RX_DMA_IRQ_DELAY_SHFT 6 + +/** + * geni_se_get_qup_hw_version() - Read the QUP wrapper Hardware version + * @se: Pointer to the corresponding serial engine. + * + * Return: Hardware Version of the wrapper. + */ +u32 geni_se_get_qup_hw_version(struct geni_se *se) +{ + struct geni_wrapper *wrapper = se->wrapper; + + return readl_relaxed(wrapper->base + QUP_HW_VER_REG); +} +EXPORT_SYMBOL(geni_se_get_qup_hw_version); + +static void geni_se_io_set_mode(void __iomem *base) +{ + u32 val; + + val = readl_relaxed(base + SE_IRQ_EN); + val |= GENI_M_IRQ_EN | GENI_S_IRQ_EN; + val |= DMA_TX_IRQ_EN | DMA_RX_IRQ_EN; + writel_relaxed(val, base + SE_IRQ_EN); + + val = readl_relaxed(base + SE_GENI_DMA_MODE_EN); + val &= ~GENI_DMA_MODE_EN; + writel_relaxed(val, base + SE_GENI_DMA_MODE_EN); + + writel_relaxed(0, base + SE_GSI_EVENT_EN); +} + +static void geni_se_io_init(void __iomem *base) +{ + u32 val; + + val = readl_relaxed(base + GENI_CGC_CTRL); + val |= DEFAULT_CGC_EN; + writel_relaxed(val, base + GENI_CGC_CTRL); + + val = readl_relaxed(base + SE_DMA_GENERAL_CFG); + val |= AHB_SEC_SLV_CLK_CGC_ON | DMA_AHB_SLV_CFG_ON; + val |= DMA_TX_CLK_CGC_ON | DMA_RX_CLK_CGC_ON; + writel_relaxed(val, base + SE_DMA_GENERAL_CFG); + + writel_relaxed(DEFAULT_IO_OUTPUT_CTRL_MSK, base + GENI_OUTPUT_CTRL); + writel_relaxed(FORCE_DEFAULT, base + GENI_FORCE_DEFAULT_REG); +} + +/** + * geni_se_init() - Initialize the GENI serial engine + * @se: Pointer to the concerned serial engine. + * @rx_wm: Receive watermark, in units of FIFO words. + * @rx_rfr_wm: Ready-for-receive watermark, in units of FIFO words. + * + * This function is used to initialize the GENI serial engine, configure + * receive watermark and ready-for-receive watermarks. + */ +void geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr) +{ + u32 val; + + geni_se_io_init(se->base); + geni_se_io_set_mode(se->base); + + writel_relaxed(rx_wm, se->base + SE_GENI_RX_WATERMARK_REG); + writel_relaxed(rx_rfr, se->base + SE_GENI_RX_RFR_WATERMARK_REG); + + val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); + val |= M_COMMON_GENI_M_IRQ_EN; + writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN); + + val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN); + val |= S_COMMON_GENI_S_IRQ_EN; + writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN); +} +EXPORT_SYMBOL(geni_se_init); + +static void geni_se_select_fifo_mode(struct geni_se *se) +{ + u32 proto = geni_se_read_proto(se); + u32 val; + + writel_relaxed(0, se->base + SE_GSI_EVENT_EN); + writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR); + writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR); + writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR); + writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR); + writel_relaxed(0xffffffff, se->base + SE_IRQ_EN); + + val = readl_relaxed(se->base + SE_GENI_M_IRQ_EN); + if (proto != GENI_SE_UART) { + val |= M_CMD_DONE_EN | M_TX_FIFO_WATERMARK_EN; + val |= M_RX_FIFO_WATERMARK_EN | M_RX_FIFO_LAST_EN; + } + writel_relaxed(val, se->base + SE_GENI_M_IRQ_EN); + + val = readl_relaxed(se->base + SE_GENI_S_IRQ_EN); + if (proto != GENI_SE_UART) + val |= S_CMD_DONE_EN; + writel_relaxed(val, se->base + SE_GENI_S_IRQ_EN); + + val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); + val &= ~GENI_DMA_MODE_EN; + writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN); +} + +static void geni_se_select_dma_mode(struct geni_se *se) +{ + u32 val; + + writel_relaxed(0, se->base + SE_GSI_EVENT_EN); + writel_relaxed(0xffffffff, se->base + SE_GENI_M_IRQ_CLEAR); + writel_relaxed(0xffffffff, se->base + SE_GENI_S_IRQ_CLEAR); + writel_relaxed(0xffffffff, se->base + SE_DMA_TX_IRQ_CLR); + writel_relaxed(0xffffffff, se->base + SE_DMA_RX_IRQ_CLR); + writel_relaxed(0xffffffff, se->base + SE_IRQ_EN); + + val = readl_relaxed(se->base + SE_GENI_DMA_MODE_EN); + val |= GENI_DMA_MODE_EN; + writel_relaxed(val, se->base + SE_GENI_DMA_MODE_EN); +} + +/** + * geni_se_select_mode() - Select the serial engine transfer mode + * @se: Pointer to the concerned serial engine. + * @mode: Transfer mode to be selected. + */ +void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode) +{ + WARN_ON(mode != GENI_SE_FIFO && mode != GENI_SE_DMA); + + switch (mode) { + case GENI_SE_FIFO: + geni_se_select_fifo_mode(se); + break; + case GENI_SE_DMA: + geni_se_select_dma_mode(se); + break; + case GENI_SE_INVALID: + default: + break; + } +} +EXPORT_SYMBOL(geni_se_select_mode); + +/** + * DOC: Overview + * + * GENI FIFO packing is highly configurable. TX/RX packing/unpacking consist + * of up to 4 operations, each operation represented by 4 configuration vectors + * of 10 bits programmed in GENI_TX_PACKING_CFG0 and GENI_TX_PACKING_CFG1 for + * TX FIFO and in GENI_RX_PACKING_CFG0 and GENI_RX_PACKING_CFG1 for RX FIFO. + * Refer to below examples for detailed bit-field description. + * + * Example 1: word_size = 7, packing_mode = 4 x 8, msb_to_lsb = 1 + * + * +-----------+-------+-------+-------+-------+ + * | | vec_0 | vec_1 | vec_2 | vec_3 | + * +-----------+-------+-------+-------+-------+ + * | start | 0x6 | 0xe | 0x16 | 0x1e | + * | direction | 1 | 1 | 1 | 1 | + * | length | 6 | 6 | 6 | 6 | + * | stop | 0 | 0 | 0 | 1 | + * +-----------+-------+-------+-------+-------+ + * + * Example 2: word_size = 15, packing_mode = 2 x 16, msb_to_lsb = 0 + * + * +-----------+-------+-------+-------+-------+ + * | | vec_0 | vec_1 | vec_2 | vec_3 | + * +-----------+-------+-------+-------+-------+ + * | start | 0x0 | 0x8 | 0x10 | 0x18 | + * | direction | 0 | 0 | 0 | 0 | + * | length | 7 | 6 | 7 | 6 | + * | stop | 0 | 0 | 0 | 1 | + * +-----------+-------+-------+-------+-------+ + * + * Example 3: word_size = 23, packing_mode = 1 x 32, msb_to_lsb = 1 + * + * +-----------+-------+-------+-------+-------+ + * | | vec_0 | vec_1 | vec_2 | vec_3 | + * +-----------+-------+-------+-------+-------+ + * | start | 0x16 | 0xe | 0x6 | 0x0 | + * | direction | 1 | 1 | 1 | 1 | + * | length | 7 | 7 | 6 | 0 | + * | stop | 0 | 0 | 1 | 0 | + * +-----------+-------+-------+-------+-------+ + * + */ + +#define NUM_PACKING_VECTORS 4 +#define PACKING_START_SHIFT 5 +#define PACKING_DIR_SHIFT 4 +#define PACKING_LEN_SHIFT 1 +#define PACKING_STOP_BIT BIT(0) +#define PACKING_VECTOR_SHIFT 10 +/** + * geni_se_config_packing() - Packing configuration of the serial engine + * @se: Pointer to the concerned serial engine + * @bpw: Bits of data per transfer word. + * @pack_words: Number of words per fifo element. + * @msb_to_lsb: Transfer from MSB to LSB or vice-versa. + * @tx_cfg: Flag to configure the TX Packing. + * @rx_cfg: Flag to configure the RX Packing. + * + * This function is used to configure the packing rules for the current + * transfer. + */ +void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words, + bool msb_to_lsb, bool tx_cfg, bool rx_cfg) +{ + u32 cfg0, cfg1, cfg[NUM_PACKING_VECTORS] = {0}; + int len; + int temp_bpw = bpw; + int idx_start = msb_to_lsb ? bpw - 1 : 0; + int idx = idx_start; + int idx_delta = msb_to_lsb ? -BITS_PER_BYTE : BITS_PER_BYTE; + int ceil_bpw = ALIGN(bpw, BITS_PER_BYTE); + int iter = (ceil_bpw * pack_words) / BITS_PER_BYTE; + int i; + + if (iter <= 0 || iter > NUM_PACKING_VECTORS) + return; + + for (i = 0; i < iter; i++) { + len = min_t(int, temp_bpw, BITS_PER_BYTE) - 1; + cfg[i] = idx << PACKING_START_SHIFT; + cfg[i] |= msb_to_lsb << PACKING_DIR_SHIFT; + cfg[i] |= len << PACKING_LEN_SHIFT; + + if (temp_bpw <= BITS_PER_BYTE) { + idx = ((i + 1) * BITS_PER_BYTE) + idx_start; + temp_bpw = bpw; + } else { + idx = idx + idx_delta; + temp_bpw = temp_bpw - BITS_PER_BYTE; + } + } + cfg[iter - 1] |= PACKING_STOP_BIT; + cfg0 = cfg[0] | (cfg[1] << PACKING_VECTOR_SHIFT); + cfg1 = cfg[2] | (cfg[3] << PACKING_VECTOR_SHIFT); + + if (tx_cfg) { + writel_relaxed(cfg0, se->base + SE_GENI_TX_PACKING_CFG0); + writel_relaxed(cfg1, se->base + SE_GENI_TX_PACKING_CFG1); + } + if (rx_cfg) { + writel_relaxed(cfg0, se->base + SE_GENI_RX_PACKING_CFG0); + writel_relaxed(cfg1, se->base + SE_GENI_RX_PACKING_CFG1); + } + + /* + * Number of protocol words in each FIFO entry + * 0 - 4x8, four words in each entry, max word size of 8 bits + * 1 - 2x16, two words in each entry, max word size of 16 bits + * 2 - 1x32, one word in each entry, max word size of 32 bits + * 3 - undefined + */ + if (pack_words || bpw == 32) + writel_relaxed(bpw / 16, se->base + SE_GENI_BYTE_GRAN); +} +EXPORT_SYMBOL(geni_se_config_packing); + +static void geni_se_clks_off(struct geni_se *se) +{ + struct geni_wrapper *wrapper = se->wrapper; + + clk_disable_unprepare(se->clk); + clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks), + wrapper->ahb_clks); +} + +/** + * geni_se_resources_off() - Turn off resources associated with the serial + * engine + * @se: Pointer to the concerned serial engine. + * + * Return: 0 on success, standard Linux error codes on failure/error. + */ +int geni_se_resources_off(struct geni_se *se) +{ + int ret; + + ret = pinctrl_pm_select_sleep_state(se->dev); + if (ret) + return ret; + + geni_se_clks_off(se); + return 0; +} +EXPORT_SYMBOL(geni_se_resources_off); + +static int geni_se_clks_on(struct geni_se *se) +{ + int ret; + struct geni_wrapper *wrapper = se->wrapper; + + ret = clk_bulk_prepare_enable(ARRAY_SIZE(wrapper->ahb_clks), + wrapper->ahb_clks); + if (ret) + return ret; + + ret = clk_prepare_enable(se->clk); + if (ret) + clk_bulk_disable_unprepare(ARRAY_SIZE(wrapper->ahb_clks), + wrapper->ahb_clks); + return ret; +} + +/** + * geni_se_resources_on() - Turn on resources associated with the serial + * engine + * @se: Pointer to the concerned serial engine. + * + * Return: 0 on success, standard Linux error codes on failure/error. + */ +int geni_se_resources_on(struct geni_se *se) +{ + int ret; + + ret = geni_se_clks_on(se); + if (ret) + return ret; + + ret = pinctrl_pm_select_default_state(se->dev); + if (ret) + geni_se_clks_off(se); + + return ret; +} +EXPORT_SYMBOL(geni_se_resources_on); + +/** + * geni_se_clk_tbl_get() - Get the clock table to program DFS + * @se: Pointer to the concerned serial engine. + * @tbl: Table in which the output is returned. + * + * This function is called by the protocol drivers to determine the different + * clock frequencies supported by serial engine core clock. The protocol + * drivers use the output to determine the clock frequency index to be + * programmed into DFS. + * + * Return: number of valid performance levels in the table on success, + * standard Linux error codes on failure. + */ +int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl) +{ + unsigned long freq = 0; + int i; + + if (se->clk_perf_tbl) { + *tbl = se->clk_perf_tbl; + return se->num_clk_levels; + } + + se->clk_perf_tbl = devm_kcalloc(se->dev, MAX_CLK_PERF_LEVEL, + sizeof(*se->clk_perf_tbl), + GFP_KERNEL); + if (!se->clk_perf_tbl) + return -ENOMEM; + + for (i = 0; i < MAX_CLK_PERF_LEVEL; i++) { + freq = clk_round_rate(se->clk, freq + 1); + if (!freq || freq == se->clk_perf_tbl[i - 1]) + break; + se->clk_perf_tbl[i] = freq; + } + se->num_clk_levels = i; + *tbl = se->clk_perf_tbl; + return se->num_clk_levels; +} +EXPORT_SYMBOL(geni_se_clk_tbl_get); + +/** + * geni_se_clk_freq_match() - Get the matching or closest SE clock frequency + * @se: Pointer to the concerned serial engine. + * @req_freq: Requested clock frequency. + * @index: Index of the resultant frequency in the table. + * @res_freq: Resultant frequency which matches or is closer to the + * requested frequency. + * @exact: Flag to indicate exact multiple requirement of the requested + * frequency. + * + * This function is called by the protocol drivers to determine the matching + * or exact multiple of the requested frequency, as provided by the serial + * engine clock in order to meet the performance requirements. If there is + * no matching or exact multiple of the requested frequency found, then it + * selects the closest floor frequency, if exact flag is not set. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq, + unsigned int *index, unsigned long *res_freq, + bool exact) +{ + unsigned long *tbl; + int num_clk_levels; + int i; + + num_clk_levels = geni_se_clk_tbl_get(se, &tbl); + if (num_clk_levels < 0) + return num_clk_levels; + + if (num_clk_levels == 0) + return -EINVAL; + + *res_freq = 0; + for (i = 0; i < num_clk_levels; i++) { + if (!(tbl[i] % req_freq)) { + *index = i; + *res_freq = tbl[i]; + return 0; + } + + if (!(*res_freq) || ((tbl[i] > *res_freq) && + (tbl[i] < req_freq))) { + *index = i; + *res_freq = tbl[i]; + } + } + + if (exact) + return -EINVAL; + + return 0; +} +EXPORT_SYMBOL(geni_se_clk_freq_match); + +#define GENI_SE_DMA_DONE_EN BIT(0) +#define GENI_SE_DMA_EOT_EN BIT(1) +#define GENI_SE_DMA_AHB_ERR_EN BIT(2) +#define GENI_SE_DMA_EOT_BUF BIT(0) +/** + * geni_se_tx_dma_prep() - Prepare the serial engine for TX DMA transfer + * @se: Pointer to the concerned serial engine. + * @buf: Pointer to the TX buffer. + * @len: Length of the TX buffer. + * @iova: Pointer to store the mapped DMA address. + * + * This function is used to prepare the buffers for DMA TX. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +int geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len, + dma_addr_t *iova) +{ + struct geni_wrapper *wrapper = se->wrapper; + u32 val; + + *iova = dma_map_single(wrapper->dev, buf, len, DMA_TO_DEVICE); + if (dma_mapping_error(wrapper->dev, *iova)) + return -EIO; + + val = GENI_SE_DMA_DONE_EN; + val |= GENI_SE_DMA_EOT_EN; + val |= GENI_SE_DMA_AHB_ERR_EN; + writel_relaxed(val, se->base + SE_DMA_TX_IRQ_EN_SET); + writel_relaxed(lower_32_bits(*iova), se->base + SE_DMA_TX_PTR_L); + writel_relaxed(upper_32_bits(*iova), se->base + SE_DMA_TX_PTR_H); + writel_relaxed(GENI_SE_DMA_EOT_BUF, se->base + SE_DMA_TX_ATTR); + writel_relaxed(len, se->base + SE_DMA_TX_LEN); + return 0; +} +EXPORT_SYMBOL(geni_se_tx_dma_prep); + +/** + * geni_se_rx_dma_prep() - Prepare the serial engine for RX DMA transfer + * @se: Pointer to the concerned serial engine. + * @buf: Pointer to the RX buffer. + * @len: Length of the RX buffer. + * @iova: Pointer to store the mapped DMA address. + * + * This function is used to prepare the buffers for DMA RX. + * + * Return: 0 on success, standard Linux error codes on failure. + */ +int geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len, + dma_addr_t *iova) +{ + struct geni_wrapper *wrapper = se->wrapper; + u32 val; + + *iova = dma_map_single(wrapper->dev, buf, len, DMA_FROM_DEVICE); + if (dma_mapping_error(wrapper->dev, *iova)) + return -EIO; + + val = GENI_SE_DMA_DONE_EN; + val |= GENI_SE_DMA_EOT_EN; + val |= GENI_SE_DMA_AHB_ERR_EN; + writel_relaxed(val, se->base + SE_DMA_RX_IRQ_EN_SET); + writel_relaxed(lower_32_bits(*iova), se->base + SE_DMA_RX_PTR_L); + writel_relaxed(upper_32_bits(*iova), se->base + SE_DMA_RX_PTR_H); + /* RX does not have EOT buffer type bit. So just reset RX_ATTR */ + writel_relaxed(0, se->base + SE_DMA_RX_ATTR); + writel_relaxed(len, se->base + SE_DMA_RX_LEN); + return 0; +} +EXPORT_SYMBOL(geni_se_rx_dma_prep); + +/** + * geni_se_tx_dma_unprep() - Unprepare the serial engine after TX DMA transfer + * @se: Pointer to the concerned serial engine. + * @iova: DMA address of the TX buffer. + * @len: Length of the TX buffer. + * + * This function is used to unprepare the DMA buffers after DMA TX. + */ +void geni_se_tx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len) +{ + struct geni_wrapper *wrapper = se->wrapper; + + if (iova && !dma_mapping_error(wrapper->dev, iova)) + dma_unmap_single(wrapper->dev, iova, len, DMA_TO_DEVICE); +} +EXPORT_SYMBOL(geni_se_tx_dma_unprep); + +/** + * geni_se_rx_dma_unprep() - Unprepare the serial engine after RX DMA transfer + * @se: Pointer to the concerned serial engine. + * @iova: DMA address of the RX buffer. + * @len: Length of the RX buffer. + * + * This function is used to unprepare the DMA buffers after DMA RX. + */ +void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len) +{ + struct geni_wrapper *wrapper = se->wrapper; + + if (iova && !dma_mapping_error(wrapper->dev, iova)) + dma_unmap_single(wrapper->dev, iova, len, DMA_FROM_DEVICE); +} +EXPORT_SYMBOL(geni_se_rx_dma_unprep); + +static int geni_se_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct geni_wrapper *wrapper; + int ret; + + wrapper = devm_kzalloc(dev, sizeof(*wrapper), GFP_KERNEL); + if (!wrapper) + return -ENOMEM; + + wrapper->dev = dev; + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + wrapper->base = devm_ioremap_resource(dev, res); + if (IS_ERR(wrapper->base)) + return PTR_ERR(wrapper->base); + + wrapper->ahb_clks[0].id = "m-ahb"; + wrapper->ahb_clks[1].id = "s-ahb"; + ret = devm_clk_bulk_get(dev, NUM_AHB_CLKS, wrapper->ahb_clks); + if (ret) { + dev_err(dev, "Err getting AHB clks %d\n", ret); + return ret; + } + + dev_set_drvdata(dev, wrapper); + dev_dbg(dev, "GENI SE Driver probed\n"); + return devm_of_platform_populate(dev); +} + +static const struct of_device_id geni_se_dt_match[] = { + { .compatible = "qcom,geni-se-qup", }, + {} +}; +MODULE_DEVICE_TABLE(of, geni_se_dt_match); + +static struct platform_driver geni_se_driver = { + .driver = { + .name = "geni_se_qup", + .of_match_table = geni_se_dt_match, + }, + .probe = geni_se_probe, +}; +module_platform_driver(geni_se_driver); + +MODULE_DESCRIPTION("GENI Serial Engine Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/qcom-geni-se.h b/include/linux/qcom-geni-se.h new file mode 100644 index 000000000000..5d6144977828 --- /dev/null +++ b/include/linux/qcom-geni-se.h @@ -0,0 +1,425 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved. + */ + +#ifndef _LINUX_QCOM_GENI_SE +#define _LINUX_QCOM_GENI_SE + +/* Transfer mode supported by GENI Serial Engines */ +enum geni_se_xfer_mode { + GENI_SE_INVALID, + GENI_SE_FIFO, + GENI_SE_DMA, +}; + +/* Protocols supported by GENI Serial Engines */ +enum geni_se_protocol_type { + GENI_SE_NONE, + GENI_SE_SPI, + GENI_SE_UART, + GENI_SE_I2C, + GENI_SE_I3C, +}; + +struct geni_wrapper; +struct clk; + +/** + * struct geni_se - GENI Serial Engine + * @base: Base Address of the Serial Engine's register block + * @dev: Pointer to the Serial Engine device + * @wrapper: Pointer to the parent QUP Wrapper core + * @clk: Handle to the core serial engine clock + * @num_clk_levels: Number of valid clock levels in clk_perf_tbl + * @clk_perf_tbl: Table of clock frequency input to serial engine clock + */ +struct geni_se { + void __iomem *base; + struct device *dev; + struct geni_wrapper *wrapper; + struct clk *clk; + unsigned int num_clk_levels; + unsigned long *clk_perf_tbl; +}; + +/* Common SE registers */ +#define GENI_FORCE_DEFAULT_REG 0x20 +#define SE_GENI_STATUS 0x40 +#define GENI_SER_M_CLK_CFG 0x48 +#define GENI_SER_S_CLK_CFG 0x4c +#define GENI_FW_REVISION_RO 0x68 +#define SE_GENI_CLK_SEL 0x7c +#define SE_GENI_DMA_MODE_EN 0x258 +#define SE_GENI_M_CMD0 0x600 +#define SE_GENI_M_CMD_CTRL_REG 0x604 +#define SE_GENI_M_IRQ_STATUS 0x610 +#define SE_GENI_M_IRQ_EN 0x614 +#define SE_GENI_M_IRQ_CLEAR 0x618 +#define SE_GENI_S_CMD0 0x630 +#define SE_GENI_S_CMD_CTRL_REG 0x634 +#define SE_GENI_S_IRQ_STATUS 0x640 +#define SE_GENI_S_IRQ_EN 0x644 +#define SE_GENI_S_IRQ_CLEAR 0x648 +#define SE_GENI_TX_FIFOn 0x700 +#define SE_GENI_RX_FIFOn 0x780 +#define SE_GENI_TX_FIFO_STATUS 0x800 +#define SE_GENI_RX_FIFO_STATUS 0x804 +#define SE_GENI_TX_WATERMARK_REG 0x80c +#define SE_GENI_RX_WATERMARK_REG 0x810 +#define SE_GENI_RX_RFR_WATERMARK_REG 0x814 +#define SE_GENI_IOS 0x908 +#define SE_DMA_TX_IRQ_STAT 0xc40 +#define SE_DMA_TX_IRQ_CLR 0xc44 +#define SE_DMA_TX_FSM_RST 0xc58 +#define SE_DMA_RX_IRQ_STAT 0xd40 +#define SE_DMA_RX_IRQ_CLR 0xd44 +#define SE_DMA_RX_FSM_RST 0xd58 +#define SE_HW_PARAM_0 0xe24 +#define SE_HW_PARAM_1 0xe28 + +/* GENI_FORCE_DEFAULT_REG fields */ +#define FORCE_DEFAULT BIT(0) + +/* GENI_STATUS fields */ +#define M_GENI_CMD_ACTIVE BIT(0) +#define S_GENI_CMD_ACTIVE BIT(12) + +/* GENI_SER_M_CLK_CFG/GENI_SER_S_CLK_CFG */ +#define SER_CLK_EN BIT(0) +#define CLK_DIV_MSK GENMASK(15, 4) +#define CLK_DIV_SHFT 4 + +/* GENI_FW_REVISION_RO fields */ +#define FW_REV_PROTOCOL_MSK GENMASK(15, 8) +#define FW_REV_PROTOCOL_SHFT 8 + +/* GENI_CLK_SEL fields */ +#define CLK_SEL_MSK GENMASK(2, 0) + +/* SE_GENI_DMA_MODE_EN */ +#define GENI_DMA_MODE_EN BIT(0) + +/* GENI_M_CMD0 fields */ +#define M_OPCODE_MSK GENMASK(31, 27) +#define M_OPCODE_SHFT 27 +#define M_PARAMS_MSK GENMASK(26, 0) + +/* GENI_M_CMD_CTRL_REG */ +#define M_GENI_CMD_CANCEL BIT(2) +#define M_GENI_CMD_ABORT BIT(1) +#define M_GENI_DISABLE BIT(0) + +/* GENI_S_CMD0 fields */ +#define S_OPCODE_MSK GENMASK(31, 27) +#define S_OPCODE_SHFT 27 +#define S_PARAMS_MSK GENMASK(26, 0) + +/* GENI_S_CMD_CTRL_REG */ +#define S_GENI_CMD_CANCEL BIT(2) +#define S_GENI_CMD_ABORT BIT(1) +#define S_GENI_DISABLE BIT(0) + +/* GENI_M_IRQ_EN fields */ +#define M_CMD_DONE_EN BIT(0) +#define M_CMD_OVERRUN_EN BIT(1) +#define M_ILLEGAL_CMD_EN BIT(2) +#define M_CMD_FAILURE_EN BIT(3) +#define M_CMD_CANCEL_EN BIT(4) +#define M_CMD_ABORT_EN BIT(5) +#define M_TIMESTAMP_EN BIT(6) +#define M_RX_IRQ_EN BIT(7) +#define M_GP_SYNC_IRQ_0_EN BIT(8) +#define M_GP_IRQ_0_EN BIT(9) +#define M_GP_IRQ_1_EN BIT(10) +#define M_GP_IRQ_2_EN BIT(11) +#define M_GP_IRQ_3_EN BIT(12) +#define M_GP_IRQ_4_EN BIT(13) +#define M_GP_IRQ_5_EN BIT(14) +#define M_IO_DATA_DEASSERT_EN BIT(22) +#define M_IO_DATA_ASSERT_EN BIT(23) +#define M_RX_FIFO_RD_ERR_EN BIT(24) +#define M_RX_FIFO_WR_ERR_EN BIT(25) +#define M_RX_FIFO_WATERMARK_EN BIT(26) +#define M_RX_FIFO_LAST_EN BIT(27) +#define M_TX_FIFO_RD_ERR_EN BIT(28) +#define M_TX_FIFO_WR_ERR_EN BIT(29) +#define M_TX_FIFO_WATERMARK_EN BIT(30) +#define M_SEC_IRQ_EN BIT(31) +#define M_COMMON_GENI_M_IRQ_EN (GENMASK(6, 1) | \ + M_IO_DATA_DEASSERT_EN | \ + M_IO_DATA_ASSERT_EN | M_RX_FIFO_RD_ERR_EN | \ + M_RX_FIFO_WR_ERR_EN | M_TX_FIFO_RD_ERR_EN | \ + M_TX_FIFO_WR_ERR_EN) + +/* GENI_S_IRQ_EN fields */ +#define S_CMD_DONE_EN BIT(0) +#define S_CMD_OVERRUN_EN BIT(1) +#define S_ILLEGAL_CMD_EN BIT(2) +#define S_CMD_FAILURE_EN BIT(3) +#define S_CMD_CANCEL_EN BIT(4) +#define S_CMD_ABORT_EN BIT(5) +#define S_GP_SYNC_IRQ_0_EN BIT(8) +#define S_GP_IRQ_0_EN BIT(9) +#define S_GP_IRQ_1_EN BIT(10) +#define S_GP_IRQ_2_EN BIT(11) +#define S_GP_IRQ_3_EN BIT(12) +#define S_GP_IRQ_4_EN BIT(13) +#define S_GP_IRQ_5_EN BIT(14) +#define S_IO_DATA_DEASSERT_EN BIT(22) +#define S_IO_DATA_ASSERT_EN BIT(23) +#define S_RX_FIFO_RD_ERR_EN BIT(24) +#define S_RX_FIFO_WR_ERR_EN BIT(25) +#define S_RX_FIFO_WATERMARK_EN BIT(26) +#define S_RX_FIFO_LAST_EN BIT(27) +#define S_COMMON_GENI_S_IRQ_EN (GENMASK(5, 1) | GENMASK(13, 9) | \ + S_RX_FIFO_RD_ERR_EN | S_RX_FIFO_WR_ERR_EN) + +/* GENI_/TX/RX/RX_RFR/_WATERMARK_REG fields */ +#define WATERMARK_MSK GENMASK(5, 0) + +/* GENI_TX_FIFO_STATUS fields */ +#define TX_FIFO_WC GENMASK(27, 0) + +/* GENI_RX_FIFO_STATUS fields */ +#define RX_LAST BIT(31) +#define RX_LAST_BYTE_VALID_MSK GENMASK(30, 28) +#define RX_LAST_BYTE_VALID_SHFT 28 +#define RX_FIFO_WC_MSK GENMASK(24, 0) + +/* SE_GENI_IOS fields */ +#define IO2_DATA_IN BIT(1) +#define RX_DATA_IN BIT(0) + +/* SE_DMA_TX_IRQ_STAT Register fields */ +#define TX_DMA_DONE BIT(0) +#define TX_EOT BIT(1) +#define TX_SBE BIT(2) +#define TX_RESET_DONE BIT(3) + +/* SE_DMA_RX_IRQ_STAT Register fields */ +#define RX_DMA_DONE BIT(0) +#define RX_EOT BIT(1) +#define RX_SBE BIT(2) +#define RX_RESET_DONE BIT(3) +#define RX_FLUSH_DONE BIT(4) +#define RX_GENI_GP_IRQ GENMASK(10, 5) +#define RX_GENI_CANCEL_IRQ BIT(11) +#define RX_GENI_GP_IRQ_EXT GENMASK(13, 12) + +/* SE_HW_PARAM_0 fields */ +#define TX_FIFO_WIDTH_MSK GENMASK(29, 24) +#define TX_FIFO_WIDTH_SHFT 24 +#define TX_FIFO_DEPTH_MSK GENMASK(21, 16) +#define TX_FIFO_DEPTH_SHFT 16 + +/* SE_HW_PARAM_1 fields */ +#define RX_FIFO_WIDTH_MSK GENMASK(29, 24) +#define RX_FIFO_WIDTH_SHFT 24 +#define RX_FIFO_DEPTH_MSK GENMASK(21, 16) +#define RX_FIFO_DEPTH_SHFT 16 + +#define HW_VER_MAJOR_MASK GENMASK(31, 28) +#define HW_VER_MAJOR_SHFT 28 +#define HW_VER_MINOR_MASK GENMASK(27, 16) +#define HW_VER_MINOR_SHFT 16 +#define HW_VER_STEP_MASK GENMASK(15, 0) + +#if IS_ENABLED(CONFIG_QCOM_GENI_SE) + +u32 geni_se_get_qup_hw_version(struct geni_se *se); + +#define geni_se_get_wrapper_version(se, major, minor, step) do { \ + u32 ver; \ +\ + ver = geni_se_get_qup_hw_version(se); \ + major = (ver & HW_VER_MAJOR_MASK) >> HW_VER_MAJOR_SHFT; \ + minor = (ver & HW_VER_MINOR_MASK) >> HW_VER_MINOR_SHFT; \ + step = version & HW_VER_STEP_MASK; \ +} while (0) + +/** + * geni_se_read_proto() - Read the protocol configured for a serial engine + * @se: Pointer to the concerned serial engine. + * + * Return: Protocol value as configured in the serial engine. + */ +static inline u32 geni_se_read_proto(struct geni_se *se) +{ + u32 val; + + val = readl_relaxed(se->base + GENI_FW_REVISION_RO); + + return (val & FW_REV_PROTOCOL_MSK) >> FW_REV_PROTOCOL_SHFT; +} + +/** + * geni_se_setup_m_cmd() - Setup the primary sequencer + * @se: Pointer to the concerned serial engine. + * @cmd: Command/Operation to setup in the primary sequencer. + * @params: Parameter for the sequencer command. + * + * This function is used to configure the primary sequencer with the + * command and its associated parameters. + */ +static inline void geni_se_setup_m_cmd(struct geni_se *se, u32 cmd, u32 params) +{ + u32 m_cmd; + + m_cmd = (cmd << M_OPCODE_SHFT) | (params & M_PARAMS_MSK); + writel_relaxed(m_cmd, se->base + SE_GENI_M_CMD0); +} + +/** + * geni_se_setup_s_cmd() - Setup the secondary sequencer + * @se: Pointer to the concerned serial engine. + * @cmd: Command/Operation to setup in the secondary sequencer. + * @params: Parameter for the sequencer command. + * + * This function is used to configure the secondary sequencer with the + * command and its associated parameters. + */ +static inline void geni_se_setup_s_cmd(struct geni_se *se, u32 cmd, u32 params) +{ + u32 s_cmd; + + s_cmd = readl_relaxed(se->base + SE_GENI_S_CMD0); + s_cmd &= ~(S_OPCODE_MSK | S_PARAMS_MSK); + s_cmd |= (cmd << S_OPCODE_SHFT); + s_cmd |= (params & S_PARAMS_MSK); + writel_relaxed(s_cmd, se->base + SE_GENI_S_CMD0); +} + +/** + * geni_se_cancel_m_cmd() - Cancel the command configured in the primary + * sequencer + * @se: Pointer to the concerned serial engine. + * + * This function is used to cancel the currently configured command in the + * primary sequencer. + */ +static inline void geni_se_cancel_m_cmd(struct geni_se *se) +{ + writel_relaxed(M_GENI_CMD_CANCEL, se->base + SE_GENI_M_CMD_CTRL_REG); +} + +/** + * geni_se_cancel_s_cmd() - Cancel the command configured in the secondary + * sequencer + * @se: Pointer to the concerned serial engine. + * + * This function is used to cancel the currently configured command in the + * secondary sequencer. + */ +static inline void geni_se_cancel_s_cmd(struct geni_se *se) +{ + writel_relaxed(S_GENI_CMD_CANCEL, se->base + SE_GENI_S_CMD_CTRL_REG); +} + +/** + * geni_se_abort_m_cmd() - Abort the command configured in the primary sequencer + * @se: Pointer to the concerned serial engine. + * + * This function is used to force abort the currently configured command in the + * primary sequencer. + */ +static inline void geni_se_abort_m_cmd(struct geni_se *se) +{ + writel_relaxed(M_GENI_CMD_ABORT, se->base + SE_GENI_M_CMD_CTRL_REG); +} + +/** + * geni_se_abort_s_cmd() - Abort the command configured in the secondary + * sequencer + * @se: Pointer to the concerned serial engine. + * + * This function is used to force abort the currently configured command in the + * secondary sequencer. + */ +static inline void geni_se_abort_s_cmd(struct geni_se *se) +{ + writel_relaxed(S_GENI_CMD_ABORT, se->base + SE_GENI_S_CMD_CTRL_REG); +} + +/** + * geni_se_get_tx_fifo_depth() - Get the TX fifo depth of the serial engine + * @se: Pointer to the concerned serial engine. + * + * This function is used to get the depth i.e. number of elements in the + * TX fifo of the serial engine. + * + * Return: TX fifo depth in units of FIFO words. + */ +static inline u32 geni_se_get_tx_fifo_depth(struct geni_se *se) +{ + u32 val; + + val = readl_relaxed(se->base + SE_HW_PARAM_0); + + return (val & TX_FIFO_DEPTH_MSK) >> TX_FIFO_DEPTH_SHFT; +} + +/** + * geni_se_get_tx_fifo_width() - Get the TX fifo width of the serial engine + * @se: Pointer to the concerned serial engine. + * + * This function is used to get the width i.e. word size per element in the + * TX fifo of the serial engine. + * + * Return: TX fifo width in bits + */ +static inline u32 geni_se_get_tx_fifo_width(struct geni_se *se) +{ + u32 val; + + val = readl_relaxed(se->base + SE_HW_PARAM_0); + + return (val & TX_FIFO_WIDTH_MSK) >> TX_FIFO_WIDTH_SHFT; +} + +/** + * geni_se_get_rx_fifo_depth() - Get the RX fifo depth of the serial engine + * @se: Pointer to the concerned serial engine. + * + * This function is used to get the depth i.e. number of elements in the + * RX fifo of the serial engine. + * + * Return: RX fifo depth in units of FIFO words + */ +static inline u32 geni_se_get_rx_fifo_depth(struct geni_se *se) +{ + u32 val; + + val = readl_relaxed(se->base + SE_HW_PARAM_1); + + return (val & RX_FIFO_DEPTH_MSK) >> RX_FIFO_DEPTH_SHFT; +} + +void geni_se_init(struct geni_se *se, u32 rx_wm, u32 rx_rfr); + +void geni_se_select_mode(struct geni_se *se, enum geni_se_xfer_mode mode); + +void geni_se_config_packing(struct geni_se *se, int bpw, int pack_words, + bool msb_to_lsb, bool tx_cfg, bool rx_cfg); + +int geni_se_resources_off(struct geni_se *se); + +int geni_se_resources_on(struct geni_se *se); + +int geni_se_clk_tbl_get(struct geni_se *se, unsigned long **tbl); + +int geni_se_clk_freq_match(struct geni_se *se, unsigned long req_freq, + unsigned int *index, unsigned long *res_freq, + bool exact); + +int geni_se_tx_dma_prep(struct geni_se *se, void *buf, size_t len, + dma_addr_t *iova); + +int geni_se_rx_dma_prep(struct geni_se *se, void *buf, size_t len, + dma_addr_t *iova); + +void geni_se_tx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len); + +void geni_se_rx_dma_unprep(struct geni_se *se, dma_addr_t iova, size_t len); +#endif +#endif -- cgit From 5ef6a16033b47afbc578c7ef8754da5ae7b198d7 Mon Sep 17 00:00:00 2001 From: "Andrew F. Davis" Date: Sat, 21 Apr 2018 18:12:49 -0500 Subject: power: supply: bq27xxx: Add support for BQ27426 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This device is software similar to the BQ27426 except it has different data memory offsets. Add support here. Signed-off-by: Andrew F. Davis Acked-by: Pali Rohár Signed-off-by: Sebastian Reichel --- Documentation/devicetree/bindings/power/supply/bq27xxx.txt | 1 + drivers/power/supply/bq27xxx_battery.c | 9 +++++++++ drivers/power/supply/bq27xxx_battery_i2c.c | 2 ++ include/linux/power/bq27xxx_battery.h | 3 ++- 4 files changed, 14 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt index 615c1cb6889f..37994fdb18ca 100644 --- a/Documentation/devicetree/bindings/power/supply/bq27xxx.txt +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.txt @@ -25,6 +25,7 @@ Required properties: * "ti,bq27545" - BQ27545 * "ti,bq27421" - BQ27421 * "ti,bq27425" - BQ27425 + * "ti,bq27426" - BQ27426 * "ti,bq27441" - BQ27441 * "ti,bq27621" - BQ27621 - reg: integer, I2C address of the fuel gauge. diff --git a/drivers/power/supply/bq27xxx_battery.c b/drivers/power/supply/bq27xxx_battery.c index 7ce60519b1bc..d44ed8e17c47 100644 --- a/drivers/power/supply/bq27xxx_battery.c +++ b/drivers/power/supply/bq27xxx_battery.c @@ -432,6 +432,7 @@ static u8 BQ27XXX_DM_REG_ROWS, }; #define bq27425_regs bq27421_regs +#define bq27426_regs bq27421_regs #define bq27441_regs bq27421_regs #define bq27621_regs bq27421_regs @@ -664,6 +665,7 @@ static enum power_supply_property bq27421_props[] = { POWER_SUPPLY_PROP_MANUFACTURER, }; #define bq27425_props bq27421_props +#define bq27426_props bq27421_props #define bq27441_props bq27421_props #define bq27621_props bq27421_props @@ -734,6 +736,12 @@ static struct bq27xxx_dm_reg bq27425_dm_regs[] = { [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 18, 2, 2800, 3700 }, }; +static struct bq27xxx_dm_reg bq27426_dm_regs[] = { + [BQ27XXX_DM_DESIGN_CAPACITY] = { 82, 6, 2, 0, 8000 }, + [BQ27XXX_DM_DESIGN_ENERGY] = { 82, 8, 2, 0, 32767 }, + [BQ27XXX_DM_TERMINATE_VOLTAGE] = { 82, 10, 2, 2500, 3700 }, +}; + #if 0 /* not yet tested */ #define bq27441_dm_regs bq27421_dm_regs #else @@ -795,6 +803,7 @@ static struct { [BQ27545] = BQ27XXX_DATA(bq27545, 0x04143672, BQ27XXX_O_OTDC), [BQ27421] = BQ27XXX_DATA(bq27421, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27425] = BQ27XXX_DATA(bq27425, 0x04143672, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP), + [BQ27426] = BQ27XXX_DATA(bq27426, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27441] = BQ27XXX_DATA(bq27441, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), [BQ27621] = BQ27XXX_DATA(bq27621, 0x80008000, BQ27XXX_O_UTOT | BQ27XXX_O_CFGUP | BQ27XXX_O_RAM), }; diff --git a/drivers/power/supply/bq27xxx_battery_i2c.c b/drivers/power/supply/bq27xxx_battery_i2c.c index 6b25e5f2337e..40069128ad44 100644 --- a/drivers/power/supply/bq27xxx_battery_i2c.c +++ b/drivers/power/supply/bq27xxx_battery_i2c.c @@ -249,6 +249,7 @@ static const struct i2c_device_id bq27xxx_i2c_id_table[] = { { "bq27545", BQ27545 }, { "bq27421", BQ27421 }, { "bq27425", BQ27425 }, + { "bq27426", BQ27426 }, { "bq27441", BQ27441 }, { "bq27621", BQ27621 }, {}, @@ -280,6 +281,7 @@ static const struct of_device_id bq27xxx_battery_i2c_of_match_table[] = { { .compatible = "ti,bq27545" }, { .compatible = "ti,bq27421" }, { .compatible = "ti,bq27425" }, + { .compatible = "ti,bq27426" }, { .compatible = "ti,bq27441" }, { .compatible = "ti,bq27621" }, {}, diff --git a/include/linux/power/bq27xxx_battery.h b/include/linux/power/bq27xxx_battery.h index 01fbf1b16258..d6355f49fbae 100644 --- a/include/linux/power/bq27xxx_battery.h +++ b/include/linux/power/bq27xxx_battery.h @@ -24,8 +24,9 @@ enum bq27xxx_chip { BQ27546, BQ27742, BQ27545, /* bq27545 */ - BQ27421, /* bq27421, bq27425, bq27441, bq27621 */ + BQ27421, /* bq27421, bq27441, bq27621 */ BQ27425, + BQ27426, BQ27441, BQ27621, }; -- cgit From 4c4268dc97c424cf6786c4ccc9acf345ba911987 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Thu, 22 Mar 2018 11:24:35 +0100 Subject: power: supply: ab8500: Drop AB8540/9540 support The AB8540 was an evolved version of the AB8500, but it was never mass produced or put into products, only reference designs exist. The upstream support was never completed and it is unlikely that this will happen so drop the support for now to simplify maintenance of the AB8500. Cc: Loic Pallardy Signed-off-by: Linus Walleij Acked-by: Lee Jones Signed-off-by: Sebastian Reichel --- drivers/power/supply/ab8500_bmdata.c | 63 -------------- drivers/power/supply/ab8500_btemp.c | 93 ++++----------------- drivers/power/supply/ab8500_charger.c | 131 ++---------------------------- drivers/power/supply/ab8500_fg.c | 12 +-- drivers/power/supply/abx500_chargalg.c | 62 -------------- include/linux/mfd/abx500.h | 1 - include/linux/mfd/abx500/ab8500-bm.h | 2 - include/linux/mfd/abx500/ux500_chargalg.h | 4 - 8 files changed, 30 insertions(+), 338 deletions(-) (limited to 'include/linux') diff --git a/drivers/power/supply/ab8500_bmdata.c b/drivers/power/supply/ab8500_bmdata.c index 4a7ed50d1dc5..7b2b69916f48 100644 --- a/drivers/power/supply/ab8500_bmdata.c +++ b/drivers/power/supply/ab8500_bmdata.c @@ -430,13 +430,6 @@ static const struct abx500_maxim_parameters ab8500_maxi_params = { .charger_curr_step = 100, }; -static const struct abx500_maxim_parameters abx540_maxi_params = { - .ena_maxi = true, - .chg_curr = 3000, - .wait_cycles = 10, - .charger_curr_step = 200, -}; - static const struct abx500_bm_charger_parameters chg = { .usb_volt_max = 5500, .usb_curr_max = 1500, @@ -453,17 +446,6 @@ static int ab8500_charge_output_curr_map[] = { 900, 1000, 1100, 1200, 1300, 1400, 1500, 1500, }; -static int ab8540_charge_output_curr_map[] = { - 0, 0, 0, 75, 100, 125, 150, 175, - 200, 225, 250, 275, 300, 325, 350, 375, - 400, 425, 450, 475, 500, 525, 550, 575, - 600, 625, 650, 675, 700, 725, 750, 775, - 800, 825, 850, 875, 900, 925, 950, 975, - 1000, 1025, 1050, 1075, 1100, 1125, 1150, 1175, - 1200, 1225, 1250, 1275, 1300, 1325, 1350, 1375, - 1400, 1425, 1450, 1500, 1600, 1700, 1900, 2000, -}; - /* * This array maps the raw hex value to charger input current used by the * AB8500 values @@ -473,17 +455,6 @@ static int ab8500_charge_input_curr_map[] = { 700, 800, 900, 1000, 1100, 1300, 1400, 1500, }; -static int ab8540_charge_input_curr_map[] = { - 25, 50, 75, 100, 125, 150, 175, 200, - 225, 250, 275, 300, 325, 350, 375, 400, - 425, 450, 475, 500, 525, 550, 575, 600, - 625, 650, 675, 700, 725, 750, 775, 800, - 825, 850, 875, 900, 925, 950, 975, 1000, - 1025, 1050, 1075, 1100, 1125, 1150, 1175, 1200, - 1225, 1250, 1275, 1300, 1325, 1350, 1375, 1400, - 1425, 1450, 1475, 1500, 1500, 1500, 1500, 1500, -}; - struct abx500_bm_data ab8500_bm_data = { .temp_under = 3, .temp_low = 8, @@ -518,40 +489,6 @@ struct abx500_bm_data ab8500_bm_data = { .n_chg_in_curr = ARRAY_SIZE(ab8500_charge_input_curr_map), }; -struct abx500_bm_data ab8540_bm_data = { - .temp_under = 3, - .temp_low = 8, - .temp_high = 43, - .temp_over = 48, - .main_safety_tmr_h = 4, - .temp_interval_chg = 20, - .temp_interval_nochg = 120, - .usb_safety_tmr_h = 4, - .bkup_bat_v = BUP_VCH_SEL_2P6V, - .bkup_bat_i = BUP_ICH_SEL_150UA, - .no_maintenance = false, - .capacity_scaling = false, - .adc_therm = ABx500_ADC_THERM_BATCTRL, - .chg_unknown_bat = false, - .enable_overshoot = false, - .fg_res = 100, - .cap_levels = &cap_levels, - .bat_type = bat_type_thermistor, - .n_btypes = ARRAY_SIZE(bat_type_thermistor), - .batt_id = 0, - .interval_charging = 5, - .interval_not_charging = 120, - .temp_hysteresis = 3, - .gnd_lift_resistance = 0, - .maxi = &abx540_maxi_params, - .chg_params = &chg, - .fg_params = &fg, - .chg_output_curr = ab8540_charge_output_curr_map, - .n_chg_out_curr = ARRAY_SIZE(ab8540_charge_output_curr_map), - .chg_input_curr = ab8540_charge_input_curr_map, - .n_chg_in_curr = ARRAY_SIZE(ab8540_charge_input_curr_map), -}; - int ab8500_bm_of_probe(struct device *dev, struct device_node *np, struct abx500_bm_data *bm) diff --git a/drivers/power/supply/ab8500_btemp.c b/drivers/power/supply/ab8500_btemp.c index f7a35ebfbab2..708fd58cd62b 100644 --- a/drivers/power/supply/ab8500_btemp.c +++ b/drivers/power/supply/ab8500_btemp.c @@ -214,22 +214,10 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, /* Only do this for batteries with internal NTC */ if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && enable) { - if (is_ab8540(di->parent)) { - if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_60UA) - curr = BAT_CTRL_60U_ENA; - else - curr = BAT_CTRL_120U_ENA; - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_16UA) - curr = BAT_CTRL_16U_ENA; - else - curr = BAT_CTRL_18U_ENA; - } else { - if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA) - curr = BAT_CTRL_7U_ENA; - else - curr = BAT_CTRL_20U_ENA; - } + if (di->curr_source == BTEMP_BATCTRL_CURR_SRC_7UA) + curr = BAT_CTRL_7U_ENA; + else + curr = BAT_CTRL_20U_ENA; dev_dbg(di->dev, "Set BATCTRL %duA\n", di->curr_source); @@ -260,28 +248,12 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, } else if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && !enable) { dev_dbg(di->dev, "Disable BATCTRL curr source\n"); - if (is_ab8540(di->parent)) { - /* Write 0 to the curr bits */ - ret = abx500_mask_and_set_register_interruptible( - di->dev, - AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, - BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA, - ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA)); - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - /* Write 0 to the curr bits */ - ret = abx500_mask_and_set_register_interruptible( - di->dev, - AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, - BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA, - ~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA)); - } else { - /* Write 0 to the curr bits */ - ret = abx500_mask_and_set_register_interruptible( - di->dev, - AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, - BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, - ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); - } + /* Write 0 to the curr bits */ + ret = abx500_mask_and_set_register_interruptible( + di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, + ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); if (ret) { dev_err(di->dev, "%s failed disabling current source\n", @@ -324,25 +296,11 @@ static int ab8500_btemp_curr_source_enable(struct ab8500_btemp *di, * if we got an error above */ disable_curr_source: - if (is_ab8540(di->parent)) { - /* Write 0 to the curr bits */ - ret = abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, - BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA, - ~(BAT_CTRL_60U_ENA | BAT_CTRL_120U_ENA)); - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - /* Write 0 to the curr bits */ - ret = abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, - BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA, - ~(BAT_CTRL_16U_ENA | BAT_CTRL_18U_ENA)); - } else { - /* Write 0 to the curr bits */ - ret = abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, - BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, - ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); - } + /* Write 0 to the curr bits */ + ret = abx500_mask_and_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_BAT_CTRL_CURRENT_SOURCE, + BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA, + ~(BAT_CTRL_7U_ENA | BAT_CTRL_20U_ENA)); if (ret) { dev_err(di->dev, "%s failed disabling current source\n", @@ -556,13 +514,8 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) { int res; u8 i; - if (is_ab8540(di->parent)) - di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA; - else if (is_ab9540(di->parent) || is_ab8505(di->parent)) - di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA; - else - di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA; + di->curr_source = BTEMP_BATCTRL_CURR_SRC_7UA; di->bm->batt_id = BATTERY_UNKNOWN; res = ab8500_btemp_get_batctrl_res(di); @@ -600,18 +553,8 @@ static int ab8500_btemp_id(struct ab8500_btemp *di) */ if (di->bm->adc_therm == ABx500_ADC_THERM_BATCTRL && di->bm->batt_id == 1) { - if (is_ab8540(di->parent)) { - dev_dbg(di->dev, - "Set BATCTRL current source to 60uA\n"); - di->curr_source = BTEMP_BATCTRL_CURR_SRC_60UA; - } else if (is_ab9540(di->parent) || is_ab8505(di->parent)) { - dev_dbg(di->dev, - "Set BATCTRL current source to 16uA\n"); - di->curr_source = BTEMP_BATCTRL_CURR_SRC_16UA; - } else { - dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n"); - di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA; - } + dev_dbg(di->dev, "Set BATCTRL current source to 20uA\n"); + di->curr_source = BTEMP_BATCTRL_CURR_SRC_20UA; } return di->bm->batt_id; diff --git a/drivers/power/supply/ab8500_charger.c b/drivers/power/supply/ab8500_charger.c index 5a76c6d343de..ba0e97284ae5 100644 --- a/drivers/power/supply/ab8500_charger.c +++ b/drivers/power/supply/ab8500_charger.c @@ -58,9 +58,7 @@ #define MAIN_CH_INPUT_CURR_SHIFT 4 #define VBUS_IN_CURR_LIM_SHIFT 4 -#define AB8540_VBUS_IN_CURR_LIM_SHIFT 2 #define AUTO_VBUS_IN_CURR_LIM_SHIFT 4 -#define AB8540_AUTO_VBUS_IN_CURR_MASK 0x3F #define VBUS_IN_CURR_LIM_RETRY_SET_TIME 30 /* seconds */ #define LED_INDICATOR_PWM_ENA 0x01 @@ -1138,10 +1136,7 @@ static int ab8500_charger_set_current(struct ab8500_charger *di, no_stepping = true; break; case AB8500_USBCH_IPT_CRNTLVL_REG: - if (is_ab8540(di->parent)) - shift_value = AB8540_VBUS_IN_CURR_LIM_SHIFT; - else - shift_value = VBUS_IN_CURR_LIM_SHIFT; + shift_value = VBUS_IN_CURR_LIM_SHIFT; prev_curr_index = (reg_value >> shift_value); curr_index = ab8500_vbus_in_curr_to_regval(di, ich); step_udelay = STEP_UDELAY * 100; @@ -1865,67 +1860,6 @@ static int ab8500_charger_update_charger_current(struct ux500_charger *charger, return ret; } -/** - * ab8540_charger_power_path_enable() - enable usb power path mode - * @charger: pointer to the ux500_charger structure - * @enable: enable/disable flag - * - * Enable or disable the power path for usb mode - * Returns error code in case of failure else 0(on success) - */ -static int ab8540_charger_power_path_enable(struct ux500_charger *charger, - bool enable) -{ - int ret; - struct ab8500_charger *di; - - if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB) - di = to_ab8500_charger_usb_device_info(charger); - else - return -ENXIO; - - ret = abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8540_USB_PP_MODE_REG, - BUS_POWER_PATH_MODE_ENA, enable); - if (ret) { - dev_err(di->dev, "%s write failed\n", __func__); - return ret; - } - - return ret; -} - - -/** - * ab8540_charger_usb_pre_chg_enable() - enable usb pre change - * @charger: pointer to the ux500_charger structure - * @enable: enable/disable flag - * - * Enable or disable the pre-chage for usb mode - * Returns error code in case of failure else 0(on success) - */ -static int ab8540_charger_usb_pre_chg_enable(struct ux500_charger *charger, - bool enable) -{ - int ret; - struct ab8500_charger *di; - - if (charger->psy->desc->type == POWER_SUPPLY_TYPE_USB) - di = to_ab8500_charger_usb_device_info(charger); - else - return -ENXIO; - - ret = abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8540_USB_PP_CHR_REG, - BUS_POWER_PATH_PRECHG_ENA, enable); - if (ret) { - dev_err(di->dev, "%s write failed\n", __func__); - return ret; - } - - return ret; -} - static int ab8500_charger_get_ext_psy_data(struct device *dev, void *data) { struct power_supply *psy; @@ -2704,23 +2638,15 @@ static void ab8500_charger_vbus_drop_end_work(struct work_struct *work) abx500_set_register_interruptible(di->dev, AB8500_CHARGER, AB8500_CHARGER_CTRL, 0x01); - if (is_ab8540(di->parent)) - ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, - AB8540_CH_USBCH_STAT3_REG, ®_value); - else - ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, - AB8500_CH_USBCH_STAT2_REG, ®_value); + ret = abx500_get_register_interruptible(di->dev, AB8500_CHARGER, + AB8500_CH_USBCH_STAT2_REG, ®_value); if (ret < 0) { dev_err(di->dev, "%s read failed\n", __func__); return; } - if (is_ab8540(di->parent)) - curr = di->bm->chg_input_curr[ - reg_value & AB8540_AUTO_VBUS_IN_CURR_MASK]; - else - curr = di->bm->chg_input_curr[ - reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT]; + curr = di->bm->chg_input_curr[ + reg_value >> AUTO_VBUS_IN_CURR_LIM_SHIFT]; if (di->max_usb_in_curr.calculated_max != curr) { /* USB source is collapsing */ @@ -3097,14 +3023,9 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) goto out; } - if (is_ab8540(di->parent)) - ret = abx500_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG, - CH_OP_CUR_LVL_2P); - else - ret = abx500_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG, - CH_OP_CUR_LVL_1P6); + ret = abx500_set_register_interruptible(di->dev, + AB8500_CHARGER, AB8500_CH_OPT_CRNTLVL_MAX_REG, + CH_OP_CUR_LVL_1P6); if (ret) { dev_err(di->dev, "failed to set CH_OPT_CRNTLVL_MAX_REG\n"); @@ -3112,8 +3033,7 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) } } - if (is_ab9540_2p0(di->parent) || is_ab9540_3p0(di->parent) - || is_ab8505_2p0(di->parent) || is_ab8540(di->parent)) + if (is_ab8505_2p0(di->parent)) ret = abx500_mask_and_set_register_interruptible(di->dev, AB8500_CHARGER, AB8500_USBCH_CTRL2_REG, @@ -3205,17 +3125,6 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) dev_err(di->dev, "failed to setup backup battery charging\n"); goto out; } - if (is_ab8540(di->parent)) { - ret = abx500_set_register_interruptible(di->dev, - AB8500_RTC, - AB8500_RTC_CTRL1_REG, - bup_vch_range | vbup33_vrtcn); - if (ret) { - dev_err(di->dev, - "failed to setup backup battery charging\n"); - goto out; - } - } /* Enable backup battery charging */ ret = abx500_mask_and_set_register_interruptible(di->dev, @@ -3226,25 +3135,6 @@ static int ab8500_charger_init_hw_registers(struct ab8500_charger *di) goto out; } - if (is_ab8540(di->parent)) { - ret = abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8540_USB_PP_MODE_REG, - BUS_VSYS_VOL_SELECT_MASK, BUS_VSYS_VOL_SELECT_3P6V); - if (ret) { - dev_err(di->dev, - "failed to setup usb power path vsys voltage\n"); - goto out; - } - ret = abx500_mask_and_set_register_interruptible(di->dev, - AB8500_CHARGER, AB8540_USB_PP_CHR_REG, - BUS_PP_PRECHG_CURRENT_MASK, 0); - if (ret) { - dev_err(di->dev, - "failed to setup usb power path precharge current\n"); - goto out; - } - } - out: return ret; } @@ -3529,8 +3419,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.ops.check_enable = &ab8500_charger_usb_check_enable; di->usb_chg.ops.kick_wd = &ab8500_charger_watchdog_kick; di->usb_chg.ops.update_curr = &ab8500_charger_update_charger_current; - di->usb_chg.ops.pp_enable = &ab8540_charger_power_path_enable; - di->usb_chg.ops.pre_chg_enable = &ab8540_charger_usb_pre_chg_enable; di->usb_chg.max_out_volt = ab8500_charger_voltage_map[ ARRAY_SIZE(ab8500_charger_voltage_map) - 1]; di->usb_chg.max_out_curr = @@ -3538,7 +3426,6 @@ static int ab8500_charger_probe(struct platform_device *pdev) di->usb_chg.wdt_refresh = CHG_WD_INTERVAL; di->usb_chg.enabled = di->bm->usb_enabled; di->usb_chg.external = false; - di->usb_chg.power_path = di->bm->usb_power_path; di->usb_state.usb_current = -1; /* Create a work queue for the charger */ diff --git a/drivers/power/supply/ab8500_fg.c b/drivers/power/supply/ab8500_fg.c index 993898d1712f..d9c6c7bedd85 100644 --- a/drivers/power/supply/ab8500_fg.c +++ b/drivers/power/supply/ab8500_fg.c @@ -2326,9 +2326,7 @@ static int ab8500_fg_init_hw_registers(struct ab8500_fg *di) goto out; } - if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && - abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) - || is_ab8540(di->parent)) { + if (is_ab8505(di->parent)) { ret = abx500_set_register_interruptible(di->dev, AB8500_RTC, AB8505_RTC_PCUT_MAX_TIME_REG, di->bm->fg_params->pcut_max_time); @@ -2915,9 +2913,7 @@ static int ab8500_fg_sysfs_psy_create_attrs(struct ab8500_fg *di) { unsigned int i; - if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && - abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) - || is_ab8540(di->parent)) { + if (is_ab8505(di->parent)) { for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) if (device_create_file(&di->fg_psy->dev, &ab8505_fg_sysfs_psy_attrs[i])) @@ -2937,9 +2933,7 @@ static void ab8500_fg_sysfs_psy_remove_attrs(struct ab8500_fg *di) { unsigned int i; - if (((is_ab8505(di->parent) || is_ab9540(di->parent)) && - abx500_get_chip_id(di->dev) >= AB8500_CUT2P0) - || is_ab8540(di->parent)) { + if (is_ab8505(di->parent)) { for (i = 0; i < ARRAY_SIZE(ab8505_fg_sysfs_psy_attrs); i++) (void)device_remove_file(&di->fg_psy->dev, &ab8505_fg_sysfs_psy_attrs[i]); diff --git a/drivers/power/supply/abx500_chargalg.c b/drivers/power/supply/abx500_chargalg.c index a4411d6bbc96..947709cdd14e 100644 --- a/drivers/power/supply/abx500_chargalg.c +++ b/drivers/power/supply/abx500_chargalg.c @@ -44,9 +44,6 @@ /* Five minutes expressed in seconds */ #define FIVE_MINUTES_IN_SECONDS 300 -/* Plus margin for the low battery threshold */ -#define BAT_PLUS_MARGIN (100) - #define CHARGALG_CURR_STEP_LOW 0 #define CHARGALG_CURR_STEP_HIGH 100 @@ -101,7 +98,6 @@ enum abx500_chargalg_states { STATE_HW_TEMP_PROTECT_INIT, STATE_HW_TEMP_PROTECT, STATE_NORMAL_INIT, - STATE_USB_PP_PRE_CHARGE, STATE_NORMAL, STATE_WAIT_FOR_RECHARGE_INIT, STATE_WAIT_FOR_RECHARGE, @@ -133,7 +129,6 @@ static const char *states[] = { "HW_TEMP_PROTECT_INIT", "HW_TEMP_PROTECT", "NORMAL_INIT", - "USB_PP_PRE_CHARGE", "NORMAL", "WAIT_FOR_RECHARGE_INIT", "WAIT_FOR_RECHARGE", @@ -603,37 +598,6 @@ static int abx500_chargalg_usb_en(struct abx500_chargalg *di, int enable, return di->usb_chg->ops.enable(di->usb_chg, enable, vset, iset); } - /** - * ab8540_chargalg_usb_pp_en() - Enable/ disable USB power path - * @di: pointer to the abx500_chargalg structure - * @enable: power path enable/disable - * - * The USB power path will be enable/ disable - */ -static int ab8540_chargalg_usb_pp_en(struct abx500_chargalg *di, bool enable) -{ - if (!di->usb_chg || !di->usb_chg->ops.pp_enable) - return -ENXIO; - - return di->usb_chg->ops.pp_enable(di->usb_chg, enable); -} - -/** - * ab8540_chargalg_usb_pre_chg_en() - Enable/ disable USB pre-charge - * @di: pointer to the abx500_chargalg structure - * @enable: USB pre-charge enable/disable - * - * The USB USB pre-charge will be enable/ disable - */ -static int ab8540_chargalg_usb_pre_chg_en(struct abx500_chargalg *di, - bool enable) -{ - if (!di->usb_chg || !di->usb_chg->ops.pre_chg_enable) - return -ENXIO; - - return di->usb_chg->ops.pre_chg_enable(di->usb_chg, enable); -} - /** * abx500_chargalg_update_chg_curr() - Update charger current * @di: pointer to the abx500_chargalg structure @@ -833,9 +797,6 @@ static void abx500_chargalg_end_of_charge(struct abx500_chargalg *di) di->batt_data.avg_curr > 0) { if (++di->eoc_cnt >= EOC_COND_CNT) { di->eoc_cnt = 0; - if ((di->chg_info.charger_type & USB_CHG) && - (di->usb_chg->power_path)) - ab8540_chargalg_usb_pp_en(di, true); di->charge_status = POWER_SUPPLY_STATUS_FULL; di->maintenance_chg = true; dev_dbg(di->dev, "EOC reached!\n"); @@ -1536,22 +1497,6 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) break; case STATE_NORMAL_INIT: - if ((di->chg_info.charger_type & USB_CHG) && - di->usb_chg->power_path) { - if (di->batt_data.volt > - (di->bm->fg_params->lowbat_threshold + - BAT_PLUS_MARGIN)) { - ab8540_chargalg_usb_pre_chg_en(di, false); - ab8540_chargalg_usb_pp_en(di, false); - } else { - ab8540_chargalg_usb_pp_en(di, true); - ab8540_chargalg_usb_pre_chg_en(di, true); - abx500_chargalg_state_to(di, - STATE_USB_PP_PRE_CHARGE); - break; - } - } - if (di->curr_status.curr_step == CHARGALG_CURR_STEP_LOW) abx500_chargalg_stop_charging(di); else { @@ -1575,13 +1520,6 @@ static void abx500_chargalg_algorithm(struct abx500_chargalg *di) break; - case STATE_USB_PP_PRE_CHARGE: - if (di->batt_data.volt > - (di->bm->fg_params->lowbat_threshold + - BAT_PLUS_MARGIN)) - abx500_chargalg_state_to(di, STATE_NORMAL_INIT); - break; - case STATE_NORMAL: handle_maxim_chg_curr(di); if (di->charge_status == POWER_SUPPLY_STATUS_FULL && diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 44412c9d26e1..aa09414756db 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -271,7 +271,6 @@ struct abx500_bm_data { bool autopower_cfg; bool ac_enabled; bool usb_enabled; - bool usb_power_path; bool no_maintenance; bool capacity_scaling; bool chg_unknown_bat; diff --git a/include/linux/mfd/abx500/ab8500-bm.h b/include/linux/mfd/abx500/ab8500-bm.h index e63681eb6c62..c06daf3d490a 100644 --- a/include/linux/mfd/abx500/ab8500-bm.h +++ b/include/linux/mfd/abx500/ab8500-bm.h @@ -248,8 +248,6 @@ enum bup_vch_sel { #define BAT_CTRL_20U_ENA 0x02 #define BAT_CTRL_18U_ENA 0x01 #define BAT_CTRL_16U_ENA 0x02 -#define BAT_CTRL_60U_ENA 0x01 -#define BAT_CTRL_120U_ENA 0x02 #define BAT_CTRL_CMP_ENA 0x04 #define FORCE_BAT_CTRL_CMP_HIGH 0x08 #define BAT_CTRL_PULL_UP_ENA 0x10 diff --git a/include/linux/mfd/abx500/ux500_chargalg.h b/include/linux/mfd/abx500/ux500_chargalg.h index 67703f23e7ba..669894f434f5 100644 --- a/include/linux/mfd/abx500/ux500_chargalg.h +++ b/include/linux/mfd/abx500/ux500_chargalg.h @@ -25,8 +25,6 @@ struct ux500_charger_ops { int (*check_enable) (struct ux500_charger *, int, int); int (*kick_wd) (struct ux500_charger *); int (*update_curr) (struct ux500_charger *, int); - int (*pp_enable) (struct ux500_charger *, bool); - int (*pre_chg_enable) (struct ux500_charger *, bool); }; /** @@ -37,7 +35,6 @@ struct ux500_charger_ops { * @max_out_curr maximum output charger current in mA * @enabled indicates if this charger is used or not * @external external charger unit (pm2xxx) - * @power_path USB power path support */ struct ux500_charger { struct power_supply *psy; @@ -47,7 +44,6 @@ struct ux500_charger { int wdt_refresh; bool enabled; bool external; - bool power_path; }; extern struct blocking_notifier_head charger_notifier_list; -- cgit From 227abcc6da7b803e380f034d6772ea0861612340 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 26 Apr 2018 08:58:27 +0300 Subject: staging: kernel.h: Prevent macro expantion bug in container_of_safe() There aren't many users of this so it doesn't cause a problem, but we obviously want to use "__mptr" here instead of "ptr" to prevent the parameter from being executed twice. Signed-off-by: Dan Carpenter Acked-by: NeilBrown Signed-off-by: Greg Kroah-Hartman --- include/linux/kernel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 58d6645b1425..3dfa3f260fc4 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -977,7 +977,7 @@ static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) && \ !__same_type(*(ptr), void), \ "pointer type mismatch in container_of()"); \ - IS_ERR_OR_NULL(ptr) ? ERR_CAST(ptr) : \ + IS_ERR_OR_NULL(__mptr) ? ERR_CAST(__mptr) : \ ((type *)(__mptr - offsetof(type, member))); }) /* Rebuild everything on CONFIG_FTRACE_MCOUNT_RECORD */ -- cgit From ab42ddb9eb71b580349b03e4fc5bfcf230422eb8 Mon Sep 17 00:00:00 2001 From: Eric Long Date: Thu, 19 Apr 2018 10:00:48 +0800 Subject: dmaengine: sprd: Move DMA request mode and interrupt type into head file This patch will move the Spreadtrum DMA request mode and interrupt type into one head file for user to configure. Signed-off-by: Eric Long Signed-off-by: Baolin Wang Signed-off-by: Vinod Koul --- drivers/dma/sprd-dma.c | 52 +--------------------------------------- include/linux/dma/sprd-dma.h | 57 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 51 deletions(-) create mode 100644 include/linux/dma/sprd-dma.h (limited to 'include/linux') diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 65ff0a583615..ccdeb8f999e5 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -125,57 +126,6 @@ #define SPRD_DMA_SOFTWARE_UID 0 -/* - * enum sprd_dma_req_mode: define the DMA request mode - * @SPRD_DMA_FRAG_REQ: fragment request mode - * @SPRD_DMA_BLK_REQ: block request mode - * @SPRD_DMA_TRANS_REQ: transaction request mode - * @SPRD_DMA_LIST_REQ: link-list request mode - * - * We have 4 types request mode: fragment mode, block mode, transaction mode - * and linklist mode. One transaction can contain several blocks, one block can - * contain several fragments. Link-list mode means we can save several DMA - * configuration into one reserved memory, then DMA can fetch each DMA - * configuration automatically to start transfer. - */ -enum sprd_dma_req_mode { - SPRD_DMA_FRAG_REQ, - SPRD_DMA_BLK_REQ, - SPRD_DMA_TRANS_REQ, - SPRD_DMA_LIST_REQ, -}; - -/* - * enum sprd_dma_int_type: define the DMA interrupt type - * @SPRD_DMA_NO_INT: do not need generate DMA interrupts. - * @SPRD_DMA_FRAG_INT: fragment done interrupt when one fragment request - * is done. - * @SPRD_DMA_BLK_INT: block done interrupt when one block request is done. - * @SPRD_DMA_BLK_FRAG_INT: block and fragment interrupt when one fragment - * or one block request is done. - * @SPRD_DMA_TRANS_INT: tansaction done interrupt when one transaction - * request is done. - * @SPRD_DMA_TRANS_FRAG_INT: transaction and fragment interrupt when one - * transaction request or fragment request is done. - * @SPRD_DMA_TRANS_BLK_INT: transaction and block interrupt when one - * transaction request or block request is done. - * @SPRD_DMA_LIST_INT: link-list done interrupt when one link-list request - * is done. - * @SPRD_DMA_CFGERR_INT: configure error interrupt when configuration is - * incorrect. - */ -enum sprd_dma_int_type { - SPRD_DMA_NO_INT, - SPRD_DMA_FRAG_INT, - SPRD_DMA_BLK_INT, - SPRD_DMA_BLK_FRAG_INT, - SPRD_DMA_TRANS_INT, - SPRD_DMA_TRANS_FRAG_INT, - SPRD_DMA_TRANS_BLK_INT, - SPRD_DMA_LIST_INT, - SPRD_DMA_CFGERR_INT, -}; - /* dma data width values */ enum sprd_dma_datawidth { SPRD_DMA_DATAWIDTH_1_BYTE, diff --git a/include/linux/dma/sprd-dma.h b/include/linux/dma/sprd-dma.h new file mode 100644 index 000000000000..c545162e725b --- /dev/null +++ b/include/linux/dma/sprd-dma.h @@ -0,0 +1,57 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef _SPRD_DMA_H_ +#define _SPRD_DMA_H_ + +/* + * enum sprd_dma_req_mode: define the DMA request mode + * @SPRD_DMA_FRAG_REQ: fragment request mode + * @SPRD_DMA_BLK_REQ: block request mode + * @SPRD_DMA_TRANS_REQ: transaction request mode + * @SPRD_DMA_LIST_REQ: link-list request mode + * + * We have 4 types request mode: fragment mode, block mode, transaction mode + * and linklist mode. One transaction can contain several blocks, one block can + * contain several fragments. Link-list mode means we can save several DMA + * configuration into one reserved memory, then DMA can fetch each DMA + * configuration automatically to start transfer. + */ +enum sprd_dma_req_mode { + SPRD_DMA_FRAG_REQ, + SPRD_DMA_BLK_REQ, + SPRD_DMA_TRANS_REQ, + SPRD_DMA_LIST_REQ, +}; + +/* + * enum sprd_dma_int_type: define the DMA interrupt type + * @SPRD_DMA_NO_INT: do not need generate DMA interrupts. + * @SPRD_DMA_FRAG_INT: fragment done interrupt when one fragment request + * is done. + * @SPRD_DMA_BLK_INT: block done interrupt when one block request is done. + * @SPRD_DMA_BLK_FRAG_INT: block and fragment interrupt when one fragment + * or one block request is done. + * @SPRD_DMA_TRANS_INT: tansaction done interrupt when one transaction + * request is done. + * @SPRD_DMA_TRANS_FRAG_INT: transaction and fragment interrupt when one + * transaction request or fragment request is done. + * @SPRD_DMA_TRANS_BLK_INT: transaction and block interrupt when one + * transaction request or block request is done. + * @SPRD_DMA_LIST_INT: link-list done interrupt when one link-list request + * is done. + * @SPRD_DMA_CFGERR_INT: configure error interrupt when configuration is + * incorrect. + */ +enum sprd_dma_int_type { + SPRD_DMA_NO_INT, + SPRD_DMA_FRAG_INT, + SPRD_DMA_BLK_INT, + SPRD_DMA_BLK_FRAG_INT, + SPRD_DMA_TRANS_INT, + SPRD_DMA_TRANS_FRAG_INT, + SPRD_DMA_TRANS_BLK_INT, + SPRD_DMA_LIST_INT, + SPRD_DMA_CFGERR_INT, +}; + +#endif -- cgit From 256c4fc76a80a69a5108069d8a09b3836bbf6542 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Sun, 22 Apr 2018 18:02:30 +0200 Subject: mtd: rawnand: add a way to pass an ID table with nand_scan() As part of the work of migrating all the drivers to nand_scan(), and because nand_scan() does not provide a way to pass an ID table, rename the function nand_scan_with_ids() and add a third parameter to give a flash ID table (like what was done with nand_scan_ident()). Create a nand_scan() helper that is just a wrapper of nand_scan_with_ids(), passing NULL as the ID table. This way a controller drivers can continue using nand_scan() transparently. Signed-off-by: Miquel Raynal Signed-off-by: Boris Brezillon --- drivers/mtd/nand/raw/nand_base.c | 10 ++++++---- include/linux/mtd/rawnand.h | 9 ++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/raw/nand_base.c b/drivers/mtd/nand/raw/nand_base.c index 72f3a89da513..414a2a3a91d0 100644 --- a/drivers/mtd/nand/raw/nand_base.c +++ b/drivers/mtd/nand/raw/nand_base.c @@ -6630,24 +6630,26 @@ EXPORT_SYMBOL(nand_scan_tail); #endif /** - * nand_scan - [NAND Interface] Scan for the NAND device + * nand_scan_with_ids - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: number of chips to scan for + * @ids: optional flash IDs table * * This fills out all the uninitialized function pointers with the defaults. * The flash ID is read and the mtd/chip structures are filled with the * appropriate values. */ -int nand_scan(struct mtd_info *mtd, int maxchips) +int nand_scan_with_ids(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *ids) { int ret; - ret = nand_scan_ident(mtd, maxchips, NULL); + ret = nand_scan_ident(mtd, maxchips, ids); if (!ret) ret = nand_scan_tail(mtd); return ret; } -EXPORT_SYMBOL(nand_scan); +EXPORT_SYMBOL(nand_scan_with_ids); /** * nand_cleanup - [NAND Interface] Free resources held by the NAND device diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index 5dad59b31244..ba8d908f5cc7 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -28,7 +28,14 @@ struct nand_flash_dev; struct device_node; /* Scan and identify a NAND device */ -int nand_scan(struct mtd_info *mtd, int max_chips); +int nand_scan_with_ids(struct mtd_info *mtd, int max_chips, + struct nand_flash_dev *ids); + +static inline int nand_scan(struct mtd_info *mtd, int max_chips) +{ + return nand_scan_with_ids(mtd, max_chips, NULL); +} + /* * Separate phases of nand_scan(), allowing board driver to intervene * and override command or ECC setup according to flash type. -- cgit From 6552d3141064f09736580dc2dc527019762cbf71 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Thu, 8 Mar 2018 11:20:45 -0800 Subject: backlight: Add RAVE SP backlight driver This driver provides access to RAVE SP backlight control functionality. Signed-off-by: Andrey Smirnov Acked-by: Daniel Thompson Signed-off-by: Lee Jones --- drivers/video/backlight/Kconfig | 6 +++ drivers/video/backlight/Makefile | 1 + drivers/video/backlight/rave-sp-backlight.c | 82 +++++++++++++++++++++++++++++ include/linux/mfd/rave-sp.h | 1 + 4 files changed, 90 insertions(+) create mode 100644 drivers/video/backlight/rave-sp-backlight.c (limited to 'include/linux') diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig index 4e1d2ad50ba1..5d2d0d7e8100 100644 --- a/drivers/video/backlight/Kconfig +++ b/drivers/video/backlight/Kconfig @@ -467,6 +467,12 @@ config BACKLIGHT_ARCXCNN If you have an ARCxCnnnn family backlight say Y to enable the backlight driver. +config BACKLIGHT_RAVE_SP + tristate "RAVE SP Backlight driver" + depends on RAVE_SP_CORE + help + Support for backlight control on RAVE SP device. + endif # BACKLIGHT_CLASS_DEVICE endif # BACKLIGHT_LCD_SUPPORT diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile index 5e28f01c8391..19da71d518bf 100644 --- a/drivers/video/backlight/Makefile +++ b/drivers/video/backlight/Makefile @@ -57,3 +57,4 @@ obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o obj-$(CONFIG_BACKLIGHT_TPS65217) += tps65217_bl.o obj-$(CONFIG_BACKLIGHT_WM831X) += wm831x_bl.o obj-$(CONFIG_BACKLIGHT_ARCXCNN) += arcxcnn_bl.o +obj-$(CONFIG_BACKLIGHT_RAVE_SP) += rave-sp-backlight.o diff --git a/drivers/video/backlight/rave-sp-backlight.c b/drivers/video/backlight/rave-sp-backlight.c new file mode 100644 index 000000000000..462f14a1b19d --- /dev/null +++ b/drivers/video/backlight/rave-sp-backlight.c @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * LCD Backlight driver for RAVE SP + * + * Copyright (C) 2018 Zodiac Inflight Innovations + * + */ + +#include +#include +#include +#include +#include + +#define RAVE_SP_BACKLIGHT_LCD_EN BIT(7) + +static int rave_sp_backlight_update_status(struct backlight_device *bd) +{ + const struct backlight_properties *p = &bd->props; + const u8 intensity = + (p->power == FB_BLANK_UNBLANK) ? p->brightness : 0; + struct rave_sp *sp = dev_get_drvdata(&bd->dev); + u8 cmd[] = { + [0] = RAVE_SP_CMD_SET_BACKLIGHT, + [1] = 0, + [2] = intensity ? RAVE_SP_BACKLIGHT_LCD_EN | intensity : 0, + [3] = 0, + [4] = 0, + }; + + return rave_sp_exec(sp, cmd, sizeof(cmd), NULL, 0); +} + +static const struct backlight_ops rave_sp_backlight_ops = { + .options = BL_CORE_SUSPENDRESUME, + .update_status = rave_sp_backlight_update_status, +}; + +static struct backlight_properties rave_sp_backlight_props = { + .type = BACKLIGHT_PLATFORM, + .max_brightness = 100, + .brightness = 50, +}; + +static int rave_sp_backlight_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct backlight_device *bd; + + bd = devm_backlight_device_register(dev, pdev->name, dev->parent, + dev_get_drvdata(dev->parent), + &rave_sp_backlight_ops, + &rave_sp_backlight_props); + if (IS_ERR(bd)) + return PTR_ERR(bd); + + backlight_update_status(bd); + + return 0; +} + +static const struct of_device_id rave_sp_backlight_of_match[] = { + { .compatible = "zii,rave-sp-backlight" }, + {} +}; + +static struct platform_driver rave_sp_backlight_driver = { + .probe = rave_sp_backlight_probe, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = rave_sp_backlight_of_match, + }, +}; +module_platform_driver(rave_sp_backlight_driver); + +MODULE_DEVICE_TABLE(of, rave_sp_backlight_of_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrey Vostrikov "); +MODULE_AUTHOR("Nikita Yushchenko "); +MODULE_AUTHOR("Andrey Smirnov "); +MODULE_DESCRIPTION("RAVE SP Backlight driver"); diff --git a/include/linux/mfd/rave-sp.h b/include/linux/mfd/rave-sp.h index 796fb9794c9e..fe0ce7bc59cf 100644 --- a/include/linux/mfd/rave-sp.h +++ b/include/linux/mfd/rave-sp.h @@ -21,6 +21,7 @@ enum rave_sp_command { RAVE_SP_CMD_STATUS = 0xA0, RAVE_SP_CMD_SW_WDT = 0xA1, RAVE_SP_CMD_PET_WDT = 0xA2, + RAVE_SP_CMD_SET_BACKLIGHT = 0xA6, RAVE_SP_CMD_RESET = 0xA7, RAVE_SP_CMD_RESET_REASON = 0xA8, -- cgit From 3157694d8c7fed9046dece8b63434cafef21cbf6 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 28 Mar 2018 19:03:25 +0200 Subject: pwm-backlight: Add support for PWM delays proprieties. Some panels (i.e. N116BGE-L41), in their power sequence specifications, request a delay between set the PWM signal and enable the backlight and between clear the PWM signal and disable the backlight. Add support for the new post-pwm-on-delay-ms and pwm-off-delay-ms proprieties to meet the timings. Signed-off-by: Enric Balletbo i Serra Acked-by: Pavel Machek Acked-by: Daniel Thompson Acked-by: Jingoo Han Acked-by: Thierry Reding Signed-off-by: Lee Jones --- drivers/video/backlight/pwm_bl.c | 19 +++++++++++++++++++ include/linux/pwm_backlight.h | 2 ++ 2 files changed, 21 insertions(+) (limited to 'include/linux') diff --git a/drivers/video/backlight/pwm_bl.c b/drivers/video/backlight/pwm_bl.c index 698ec68bcdc9..8e3f1245f5c5 100644 --- a/drivers/video/backlight/pwm_bl.c +++ b/drivers/video/backlight/pwm_bl.c @@ -10,6 +10,7 @@ * published by the Free Software Foundation. */ +#include #include #include #include @@ -35,6 +36,8 @@ struct pwm_bl_data { struct gpio_desc *enable_gpio; unsigned int scale; bool legacy; + unsigned int post_pwm_on_delay; + unsigned int pwm_off_delay; int (*notify)(struct device *, int brightness); void (*notify_after)(struct device *, @@ -56,6 +59,9 @@ static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness) pwm_enable(pb->pwm); + if (pb->post_pwm_on_delay) + msleep(pb->post_pwm_on_delay); + if (pb->enable_gpio) gpiod_set_value_cansleep(pb->enable_gpio, 1); @@ -70,6 +76,9 @@ static void pwm_backlight_power_off(struct pwm_bl_data *pb) if (pb->enable_gpio) gpiod_set_value_cansleep(pb->enable_gpio, 0); + if (pb->pwm_off_delay) + msleep(pb->pwm_off_delay); + pwm_config(pb->pwm, 0, pb->period); pwm_disable(pb->pwm); @@ -178,6 +187,14 @@ static int pwm_backlight_parse_dt(struct device *dev, data->max_brightness--; } + /* + * These values are optional and set as 0 by default, the out values + * are modified only if a valid u32 value can be decoded. + */ + of_property_read_u32(node, "post-pwm-on-delay-ms", + &data->post_pwm_on_delay); + of_property_read_u32(node, "pwm-off-delay-ms", &data->pwm_off_delay); + data->enable_gpio = -EINVAL; return 0; } @@ -276,6 +293,8 @@ static int pwm_backlight_probe(struct platform_device *pdev) pb->exit = data->exit; pb->dev = &pdev->dev; pb->enabled = false; + pb->post_pwm_on_delay = data->post_pwm_on_delay; + pb->pwm_off_delay = data->pwm_off_delay; pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable", GPIOD_ASIS); diff --git a/include/linux/pwm_backlight.h b/include/linux/pwm_backlight.h index e8afbd71a140..8ea265a022fd 100644 --- a/include/linux/pwm_backlight.h +++ b/include/linux/pwm_backlight.h @@ -14,6 +14,8 @@ struct platform_pwm_backlight_data { unsigned int lth_brightness; unsigned int pwm_period_ns; unsigned int *levels; + unsigned int post_pwm_on_delay; + unsigned int pwm_off_delay; /* TODO remove once all users are switched to gpiod_* API */ int enable_gpio; int (*init)(struct device *dev); -- cgit From 3a0c991fe3b48be02373102b1d06cb99934f0bee Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Apr 2018 19:42:48 +0200 Subject: backlight: Nuke unused backlight.props.state states The backlight power state handling is supremely confusing. We have: - props.power, using FB_BLANK_* defines - props.fb_blank, using the same, but deprecated int favour of props.state - props.state, using the BL_CORE_* defines - and finally a bunch of backlight drivers treat brightness == 0 as off. But of course not all of them. This is way too much confusion to fix in a simple patch, but at least prevent more hilarity from spreading by removing the unused BL_CORE_* defines. I have no idea why exactly anyone would need that. Wrt the ideal state, we really just want a boolean state. The 4 power saving states that the fbdev subsystem uses are overkill in todays hw (this was only relevant for VGA and similar analog circuits like TV-out), the new drm atomic modeset api simplified even the uapi to a simple bool. And there was never a valid technical reason to have the intermediate fbdev power states for backlights (those really only can be either off or on). Cleanup motivated by Meghana's questions about all this. Signed-off-by: Daniel Vetter Acked-by: Daniel Thompson Acked-by: Jingoo Han Reviewed-by: Jani Nikula Signed-off-by: Lee Jones --- include/linux/backlight.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/backlight.h b/include/linux/backlight.h index 2baab6f3861d..1db67662bfcb 100644 --- a/include/linux/backlight.h +++ b/include/linux/backlight.h @@ -84,9 +84,6 @@ struct backlight_properties { #define BL_CORE_SUSPENDED (1 << 0) /* backlight is suspended */ #define BL_CORE_FBBLANK (1 << 1) /* backlight is under an fb blank event */ -#define BL_CORE_DRIVER4 (1 << 28) /* reserved for driver specific use */ -#define BL_CORE_DRIVER3 (1 << 29) /* reserved for driver specific use */ -#define BL_CORE_DRIVER2 (1 << 30) /* reserved for driver specific use */ #define BL_CORE_DRIVER1 (1 << 31) /* reserved for driver specific use */ }; -- cgit From 3cf91adaa594e8933af1727942ac560e5c7bc70e Mon Sep 17 00:00:00 2001 From: Daniel Vetter Date: Wed, 25 Apr 2018 19:42:52 +0200 Subject: backlight: Nuke BL_CORE_DRIVER1 Now that the 3 drivers using this are cleaned up we can also remove this final bit of confusion of leaking driver internals into the backlight power interface. The backlight power interface itself is still a massive mess. Signed-off-by: Daniel Vetter Acked-by: Daniel Thompson Reviewed-by: Jani Nikula Signed-off-by: Lee Jones --- include/linux/backlight.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/backlight.h b/include/linux/backlight.h index 1db67662bfcb..7fbf0539e14a 100644 --- a/include/linux/backlight.h +++ b/include/linux/backlight.h @@ -84,7 +84,6 @@ struct backlight_properties { #define BL_CORE_SUSPENDED (1 << 0) /* backlight is suspended */ #define BL_CORE_FBBLANK (1 << 1) /* backlight is under an fb blank event */ -#define BL_CORE_DRIVER1 (1 << 31) /* reserved for driver specific use */ }; -- cgit From e7420c2d4495cbb9c14dd8bf8b3b4e5bdded6e20 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Mon, 16 Apr 2018 10:26:46 -0700 Subject: bus: ti-sysc: Tag some modules resource providers for noirq suspend Modules that provide resources for other modules need to be suspended and resumed in the noirq calls. Tag the resource providing modules. Signed-off-by: Tony Lindgren --- drivers/bus/ti-sysc.c | 77 +++++++++++++++++++++++++++++++++++ include/linux/platform_data/ti-sysc.h | 1 + 2 files changed, 78 insertions(+) (limited to 'include/linux') diff --git a/drivers/bus/ti-sysc.c b/drivers/bus/ti-sysc.c index f27b182384cd..1f90b91dbfae 100644 --- a/drivers/bus/ti-sysc.c +++ b/drivers/bus/ti-sysc.c @@ -665,6 +665,10 @@ static int sysc_suspend(struct device *dev) ddata = dev_get_drvdata(dev); + if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER | + SYSC_QUIRK_LEGACY_IDLE)) + return 0; + if (!ddata->enabled) return 0; @@ -678,6 +682,58 @@ static int sysc_resume(struct device *dev) struct sysc *ddata; ddata = dev_get_drvdata(dev); + + if (ddata->cfg.quirks & (SYSC_QUIRK_RESOURCE_PROVIDER | + SYSC_QUIRK_LEGACY_IDLE)) + return 0; + + if (ddata->needs_resume) { + dev_dbg(ddata->dev, "%s %s\n", __func__, + ddata->name ? ddata->name : ""); + + ddata->needs_resume = false; + + return sysc_runtime_resume(dev); + } + + return 0; +} + +static int sysc_noirq_suspend(struct device *dev) +{ + struct sysc *ddata; + + ddata = dev_get_drvdata(dev); + + if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) + return 0; + + if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER)) + return 0; + + if (!ddata->enabled) + return 0; + + dev_dbg(ddata->dev, "%s %s\n", __func__, + ddata->name ? ddata->name : ""); + + ddata->needs_resume = true; + + return sysc_runtime_suspend(dev); +} + +static int sysc_noirq_resume(struct device *dev) +{ + struct sysc *ddata; + + ddata = dev_get_drvdata(dev); + + if (ddata->cfg.quirks & SYSC_QUIRK_LEGACY_IDLE) + return 0; + + if (!(ddata->cfg.quirks & SYSC_QUIRK_RESOURCE_PROVIDER)) + return 0; + if (ddata->needs_resume) { ddata->needs_resume = false; @@ -690,6 +746,7 @@ static int sysc_resume(struct device *dev) static const struct dev_pm_ops sysc_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(sysc_suspend, sysc_resume) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(sysc_noirq_suspend, sysc_noirq_resume) SET_RUNTIME_PM_OPS(sysc_runtime_suspend, sysc_runtime_resume, NULL) @@ -721,6 +778,26 @@ struct sysc_revision_quirk { } static const struct sysc_revision_quirk sysc_revision_quirks[] = { + /* These need to use noirq_suspend */ + SYSC_QUIRK("control", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("i2c", 0, 0, 0x10, 0x90, 0x5040000a, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("mcspi", 0, 0, 0x10, -1, 0x40300a0b, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("prcm", 0, 0, -1, -1, 0x40000100, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("ocp2scp", 0, 0, 0x10, 0x14, 0x50060005, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("padconf", 0, 0, 0x10, -1, 0x4fff0800, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("scm", 0, 0, 0x10, -1, 0x40000900, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("scrm", 0, 0, -1, -1, 0x00000010, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + SYSC_QUIRK("sdma", 0, 0, 0x2c, 0x28, 0x00010900, 0xffffffff, + SYSC_QUIRK_RESOURCE_PROVIDER), + /* These drivers need to be fixed to not use pm_runtime_irq_safe() */ SYSC_QUIRK("gpio", 0, 0, 0x10, 0x114, 0x50600801, 0xffffffff, SYSC_QUIRK_LEGACY_IDLE | SYSC_QUIRK_OPT_CLKS_IN_RESET), diff --git a/include/linux/platform_data/ti-sysc.h b/include/linux/platform_data/ti-sysc.h index 80ce28d40832..990aad477458 100644 --- a/include/linux/platform_data/ti-sysc.h +++ b/include/linux/platform_data/ti-sysc.h @@ -45,6 +45,7 @@ struct sysc_regbits { s8 emufree_shift; }; +#define SYSC_QUIRK_RESOURCE_PROVIDER BIT(9) #define SYSC_QUIRK_LEGACY_IDLE BIT(8) #define SYSC_QUIRK_RESET_STATUS BIT(7) #define SYSC_QUIRK_NO_IDLE_ON_INIT BIT(6) -- cgit From bde1a3d84634f98151e3f748ab90865e9f544b10 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Mon, 30 Apr 2018 10:24:42 +0200 Subject: mtd: rawnand: davinci: store the core chipselect number in platform data We have the 'ti,davinci-chipselect' property in the device tree, but when using platform data the driver silently uses the id field of struct platform_device as the chipselect. This is confusing and we almost broke the nand support again recently after converting the platform to common clock framework (which changed the device id in the clock lookup - the problem is gone now that we no longer acquire the clock in the nand driver. This patch adds a new field - core_chipsel - to the platform_data. Subsequent patches will convert the platforms to using this new field. Acked-by: Boris Brezillon Signed-off-by: Bartosz Golaszewski Signed-off-by: Sekhar Nori --- include/linux/platform_data/mtd-davinci.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/linux') diff --git a/include/linux/platform_data/mtd-davinci.h b/include/linux/platform_data/mtd-davinci.h index f1a2cf655bdb..1bbfa27cccb4 100644 --- a/include/linux/platform_data/mtd-davinci.h +++ b/include/linux/platform_data/mtd-davinci.h @@ -56,6 +56,16 @@ struct davinci_nand_pdata { /* platform_data */ uint32_t mask_ale; uint32_t mask_cle; + /* + * 0-indexed chip-select number of the asynchronous + * interface to which the NAND device has been connected. + * + * So, if you have NAND connected to CS3 of DA850, you + * will pass '1' here. Since the asynchronous interface + * on DA850 starts from CS2. + */ + uint32_t core_chipsel; + /* for packages using two chipselects */ uint32_t mask_chipsel; -- cgit From eb06d6bbc45a7561de78a00fb17bfbb75893ee26 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Wed, 18 Apr 2018 16:50:01 +0200 Subject: clk: Extract OF clock helpers in The use of of_clk_get_parent_{count,name}() and of_clk_init() is not limited to clock providers. Hence move these helpers into their own header file, so callers that are not clock providers no longer have to include . Suggested-by: Stephen Boyd Signed-off-by: Geert Uytterhoeven Reviewed-by: Heiko Stuebner Signed-off-by: Stephen Boyd --- MAINTAINERS | 1 + include/linux/clk-provider.h | 14 +------------- include/linux/of_clk.h | 30 ++++++++++++++++++++++++++++++ 3 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 include/linux/of_clk.h (limited to 'include/linux') diff --git a/MAINTAINERS b/MAINTAINERS index 0a1410d5a621..b61b2bf1eb75 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3556,6 +3556,7 @@ F: drivers/clk/ X: drivers/clk/clkdev.c F: include/linux/clk-pr* F: include/linux/clk/ +F: include/linux/of_clk.h COMMON INTERNET FILE SYSTEM (CIFS) M: Steve French diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 210a890008f9..61cb4729f22a 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -13,6 +13,7 @@ #include #include +#include #ifdef CONFIG_COMMON_CLK @@ -890,13 +891,10 @@ struct clk_hw *of_clk_hw_simple_get(struct of_phandle_args *clkspec, struct clk *of_clk_src_onecell_get(struct of_phandle_args *clkspec, void *data); struct clk_hw *of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data); -unsigned int of_clk_get_parent_count(struct device_node *np); int of_clk_parent_fill(struct device_node *np, const char **parents, unsigned int size); -const char *of_clk_get_parent_name(struct device_node *np, int index); int of_clk_detect_critical(struct device_node *np, int index, unsigned long *flags); -void of_clk_init(const struct of_device_id *matches); #else /* !CONFIG_OF */ @@ -943,26 +941,16 @@ of_clk_hw_onecell_get(struct of_phandle_args *clkspec, void *data) { return ERR_PTR(-ENOENT); } -static inline unsigned int of_clk_get_parent_count(struct device_node *np) -{ - return 0; -} static inline int of_clk_parent_fill(struct device_node *np, const char **parents, unsigned int size) { return 0; } -static inline const char *of_clk_get_parent_name(struct device_node *np, - int index) -{ - return NULL; -} static inline int of_clk_detect_critical(struct device_node *np, int index, unsigned long *flags) { return 0; } -static inline void of_clk_init(const struct of_device_id *matches) {} #endif /* CONFIG_OF */ /* diff --git a/include/linux/of_clk.h b/include/linux/of_clk.h new file mode 100644 index 000000000000..b27da9f164cb --- /dev/null +++ b/include/linux/of_clk.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * OF clock helpers + */ + +#ifndef __LINUX_OF_CLK_H +#define __LINUX_OF_CLK_H + +#if defined(CONFIG_COMMON_CLK) && defined(CONFIG_OF) + +unsigned int of_clk_get_parent_count(struct device_node *np); +const char *of_clk_get_parent_name(struct device_node *np, int index); +void of_clk_init(const struct of_device_id *matches); + +#else /* !CONFIG_COMMON_CLK || !CONFIG_OF */ + +static inline unsigned int of_clk_get_parent_count(struct device_node *np) +{ + return 0; +} +static inline const char *of_clk_get_parent_name(struct device_node *np, + int index) +{ + return NULL; +} +static inline void of_clk_init(const struct of_device_id *matches) {} + +#endif /* !CONFIG_COMMON_CLK || !CONFIG_OF */ + +#endif /* __LINUX_OF_CLK_H */ -- cgit From 0cb8dae4a0df2a977847c2dc6766a7783ce50f9d Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 18 Apr 2018 11:09:47 -0700 Subject: fscrypt: allow synchronous bio decryption Currently, fscrypt provides fscrypt_decrypt_bio_pages() which decrypts a bio's pages asynchronously, then unlocks them afterwards. But, this assumes that decryption is the last "postprocessing step" for the bio, so it's incompatible with additional postprocessing steps such as authenticity verification after decryption. Therefore, rename the existing fscrypt_decrypt_bio_pages() to fscrypt_enqueue_decrypt_bio(). Then, add fscrypt_decrypt_bio() which decrypts the pages in the bio synchronously without unlocking the pages, nor setting them Uptodate; and add fscrypt_enqueue_decrypt_work(), which enqueues work on the fscrypt_read_workqueue. The new functions will be used by filesystems that support both fscrypt and fs-verity. Signed-off-by: Eric Biggers Signed-off-by: Jaegeuk Kim --- fs/crypto/bio.c | 35 ++++++++++++++++++++++------------- fs/crypto/crypto.c | 8 +++++++- fs/crypto/fscrypt_private.h | 1 - fs/ext4/readpage.c | 2 +- fs/f2fs/data.c | 2 +- include/linux/fscrypt_notsupp.h | 13 ++++++++++--- include/linux/fscrypt_supp.h | 5 ++++- 7 files changed, 45 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/fs/crypto/bio.c b/fs/crypto/bio.c index 0d5e6a569d58..0959044c5cee 100644 --- a/fs/crypto/bio.c +++ b/fs/crypto/bio.c @@ -26,15 +26,8 @@ #include #include "fscrypt_private.h" -/* - * Call fscrypt_decrypt_page on every single page, reusing the encryption - * context. - */ -static void completion_pages(struct work_struct *work) +static void __fscrypt_decrypt_bio(struct bio *bio, bool done) { - struct fscrypt_ctx *ctx = - container_of(work, struct fscrypt_ctx, r.work); - struct bio *bio = ctx->r.bio; struct bio_vec *bv; int i; @@ -46,22 +39,38 @@ static void completion_pages(struct work_struct *work) if (ret) { WARN_ON_ONCE(1); SetPageError(page); - } else { + } else if (done) { SetPageUptodate(page); } - unlock_page(page); + if (done) + unlock_page(page); } +} + +void fscrypt_decrypt_bio(struct bio *bio) +{ + __fscrypt_decrypt_bio(bio, false); +} +EXPORT_SYMBOL(fscrypt_decrypt_bio); + +static void completion_pages(struct work_struct *work) +{ + struct fscrypt_ctx *ctx = + container_of(work, struct fscrypt_ctx, r.work); + struct bio *bio = ctx->r.bio; + + __fscrypt_decrypt_bio(bio, true); fscrypt_release_ctx(ctx); bio_put(bio); } -void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, struct bio *bio) +void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, struct bio *bio) { INIT_WORK(&ctx->r.work, completion_pages); ctx->r.bio = bio; - queue_work(fscrypt_read_workqueue, &ctx->r.work); + fscrypt_enqueue_decrypt_work(&ctx->r.work); } -EXPORT_SYMBOL(fscrypt_decrypt_bio_pages); +EXPORT_SYMBOL(fscrypt_enqueue_decrypt_bio); void fscrypt_pullback_bio_page(struct page **page, bool restore) { diff --git a/fs/crypto/crypto.c b/fs/crypto/crypto.c index ce654526c0fb..0758d32ad01b 100644 --- a/fs/crypto/crypto.c +++ b/fs/crypto/crypto.c @@ -45,12 +45,18 @@ static mempool_t *fscrypt_bounce_page_pool = NULL; static LIST_HEAD(fscrypt_free_ctxs); static DEFINE_SPINLOCK(fscrypt_ctx_lock); -struct workqueue_struct *fscrypt_read_workqueue; +static struct workqueue_struct *fscrypt_read_workqueue; static DEFINE_MUTEX(fscrypt_init_mutex); static struct kmem_cache *fscrypt_ctx_cachep; struct kmem_cache *fscrypt_info_cachep; +void fscrypt_enqueue_decrypt_work(struct work_struct *work) +{ + queue_work(fscrypt_read_workqueue, work); +} +EXPORT_SYMBOL(fscrypt_enqueue_decrypt_work); + /** * fscrypt_release_ctx() - Releases an encryption context * @ctx: The encryption context to release. diff --git a/fs/crypto/fscrypt_private.h b/fs/crypto/fscrypt_private.h index ad6722bae8b7..4012558f6115 100644 --- a/fs/crypto/fscrypt_private.h +++ b/fs/crypto/fscrypt_private.h @@ -97,7 +97,6 @@ static inline bool fscrypt_valid_enc_modes(u32 contents_mode, /* crypto.c */ extern struct kmem_cache *fscrypt_info_cachep; extern int fscrypt_initialize(unsigned int cop_flags); -extern struct workqueue_struct *fscrypt_read_workqueue; extern int fscrypt_do_page_crypto(const struct inode *inode, fscrypt_direction_t rw, u64 lblk_num, struct page *src_page, diff --git a/fs/ext4/readpage.c b/fs/ext4/readpage.c index 9ffa6fad18db..19b87a8de6ff 100644 --- a/fs/ext4/readpage.c +++ b/fs/ext4/readpage.c @@ -77,7 +77,7 @@ static void mpage_end_io(struct bio *bio) if (bio->bi_status) { fscrypt_release_ctx(bio->bi_private); } else { - fscrypt_decrypt_bio_pages(bio->bi_private, bio); + fscrypt_enqueue_decrypt_bio(bio->bi_private, bio); return; } } diff --git a/fs/f2fs/data.c b/fs/f2fs/data.c index 02237d4d91f5..39225519de1c 100644 --- a/fs/f2fs/data.c +++ b/fs/f2fs/data.c @@ -66,7 +66,7 @@ static void f2fs_read_end_io(struct bio *bio) if (bio->bi_status) { fscrypt_release_ctx(bio->bi_private); } else { - fscrypt_decrypt_bio_pages(bio->bi_private, bio); + fscrypt_enqueue_decrypt_bio(bio->bi_private, bio); return; } } diff --git a/include/linux/fscrypt_notsupp.h b/include/linux/fscrypt_notsupp.h index 44b50c04bae9..9770be37c9d4 100644 --- a/include/linux/fscrypt_notsupp.h +++ b/include/linux/fscrypt_notsupp.h @@ -25,6 +25,10 @@ static inline bool fscrypt_dummy_context_enabled(struct inode *inode) } /* crypto.c */ +static inline void fscrypt_enqueue_decrypt_work(struct work_struct *work) +{ +} + static inline struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *inode, gfp_t gfp_flags) { @@ -160,10 +164,13 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname, } /* bio.c */ -static inline void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *ctx, - struct bio *bio) +static inline void fscrypt_decrypt_bio(struct bio *bio) +{ +} + +static inline void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, + struct bio *bio) { - return; } static inline void fscrypt_pullback_bio_page(struct page **page, bool restore) diff --git a/include/linux/fscrypt_supp.h b/include/linux/fscrypt_supp.h index 477a7a6504d2..2c9a86ac5e83 100644 --- a/include/linux/fscrypt_supp.h +++ b/include/linux/fscrypt_supp.h @@ -59,6 +59,7 @@ static inline bool fscrypt_dummy_context_enabled(struct inode *inode) } /* crypto.c */ +extern void fscrypt_enqueue_decrypt_work(struct work_struct *); extern struct fscrypt_ctx *fscrypt_get_ctx(const struct inode *, gfp_t); extern void fscrypt_release_ctx(struct fscrypt_ctx *); extern struct page *fscrypt_encrypt_page(const struct inode *, struct page *, @@ -188,7 +189,9 @@ static inline bool fscrypt_match_name(const struct fscrypt_name *fname, } /* bio.c */ -extern void fscrypt_decrypt_bio_pages(struct fscrypt_ctx *, struct bio *); +extern void fscrypt_decrypt_bio(struct bio *); +extern void fscrypt_enqueue_decrypt_bio(struct fscrypt_ctx *ctx, + struct bio *bio); extern void fscrypt_pullback_bio_page(struct page **, bool); extern int fscrypt_zeroout_range(const struct inode *, pgoff_t, sector_t, unsigned int); -- cgit From 1e0a601437a6111ecf384df010812c53cada6497 Mon Sep 17 00:00:00 2001 From: Lokesh Vutla Date: Fri, 4 May 2018 23:10:23 -0700 Subject: firmware: ti_sci: Switch to SPDX Licensing Switch to SPDX licensing and drop the GPL text which comes redundant. Acked-by: Nishanth Menon Signed-off-by: Lokesh Vutla Signed-off-by: Santosh Shilimkar --- drivers/firmware/ti_sci.c | 10 +--------- drivers/firmware/ti_sci.h | 30 +----------------------------- include/linux/soc/ti/ti_sci_protocol.h | 10 +--------- 3 files changed, 3 insertions(+), 47 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/ti_sci.c b/drivers/firmware/ti_sci.c index 5229036dcfbf..b74a533ef35b 100644 --- a/drivers/firmware/ti_sci.c +++ b/drivers/firmware/ti_sci.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Texas Instruments System Control Interface Protocol Driver * * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ * Nishanth Menon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #define pr_fmt(fmt) "%s: " fmt, __func__ diff --git a/drivers/firmware/ti_sci.h b/drivers/firmware/ti_sci.h index 9b611e9e6f6d..12bf316b68df 100644 --- a/drivers/firmware/ti_sci.h +++ b/drivers/firmware/ti_sci.h @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: BSD-3-Clause /* * Texas Instruments System Control Interface (TISCI) Protocol * @@ -6,35 +7,6 @@ * See: http://processors.wiki.ti.com/index.php/TISCI for details * * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the - * distribution. - * - * Neither the name of Texas Instruments Incorporated nor the names of - * its contributors may be used to endorse or promote products derived - * from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * */ #ifndef __TI_SCI_H diff --git a/include/linux/soc/ti/ti_sci_protocol.h b/include/linux/soc/ti/ti_sci_protocol.h index 0ccbc138c26a..18435e5c6364 100644 --- a/include/linux/soc/ti/ti_sci_protocol.h +++ b/include/linux/soc/ti/ti_sci_protocol.h @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Texas Instruments System Control Interface Protocol * * Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/ * Nishanth Menon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __TISCI_PROTOCOL_H -- cgit From a2268cfbf599e7f55d4ee68193f08b4f44535fac Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 4 May 2018 15:34:32 -0400 Subject: xprtrdma: Add proper SPDX tags for NetApp-contributed source Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/rpc_rdma.h | 1 + include/linux/sunrpc/xprtrdma.h | 1 + net/sunrpc/xprtrdma/module.c | 1 + net/sunrpc/xprtrdma/rpc_rdma.c | 1 + net/sunrpc/xprtrdma/transport.c | 1 + net/sunrpc/xprtrdma/verbs.c | 1 + net/sunrpc/xprtrdma/xprt_rdma.h | 1 + 7 files changed, 7 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/rpc_rdma.h b/include/linux/sunrpc/rpc_rdma.h index 8f144db73e38..92d182fd8e3b 100644 --- a/include/linux/sunrpc/rpc_rdma.h +++ b/include/linux/sunrpc/rpc_rdma.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (c) 2015-2017 Oracle. All rights reserved. * Copyright (c) 2003-2007 Network Appliance, Inc. All rights reserved. diff --git a/include/linux/sunrpc/xprtrdma.h b/include/linux/sunrpc/xprtrdma.h index 5859563e3c1f..86fc38ff0355 100644 --- a/include/linux/sunrpc/xprtrdma.h +++ b/include/linux/sunrpc/xprtrdma.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (c) 2003-2007 Network Appliance, Inc. All rights reserved. * diff --git a/net/sunrpc/xprtrdma/module.c b/net/sunrpc/xprtrdma/module.c index a762d192372b..f338065121f2 100644 --- a/net/sunrpc/xprtrdma/module.c +++ b/net/sunrpc/xprtrdma/module.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2015, 2017 Oracle. All rights reserved. */ diff --git a/net/sunrpc/xprtrdma/rpc_rdma.c b/net/sunrpc/xprtrdma/rpc_rdma.c index e8adad33d0bb..8f89e3faae8e 100644 --- a/net/sunrpc/xprtrdma/rpc_rdma.c +++ b/net/sunrpc/xprtrdma/rpc_rdma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2014-2017 Oracle. All rights reserved. * Copyright (c) 2003-2007 Network Appliance, Inc. All rights reserved. diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index cc1aad325496..4717578b4b81 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2014-2017 Oracle. All rights reserved. * Copyright (c) 2003-2007 Network Appliance, Inc. All rights reserved. diff --git a/net/sunrpc/xprtrdma/verbs.c b/net/sunrpc/xprtrdma/verbs.c index fe5eaca2d197..b2c6f525b020 100644 --- a/net/sunrpc/xprtrdma/verbs.c +++ b/net/sunrpc/xprtrdma/verbs.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2014-2017 Oracle. All rights reserved. * Copyright (c) 2003-2007 Network Appliance, Inc. All rights reserved. diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 3d3b423fa9c1..3490a87bc69a 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (c) 2014-2017 Oracle. All rights reserved. * Copyright (c) 2003-2007 Network Appliance, Inc. All rights reserved. -- cgit From 37ac86c3a76c113619b7d9afe0251bbfc04cb80a Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 4 May 2018 15:34:53 -0400 Subject: SUNRPC: Initialize rpc_rqst outside of xprt->reserve_lock alloc_slot is a transport-specific op, but initializing an rpc_rqst is common to all transports. In addition, the only part of initial- izing an rpc_rqst that needs serialization is getting a fresh XID. Move rpc_rqst initialization to common code in preparation for adding a transport-specific alloc_slot to xprtrdma. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/xprt.h | 1 + net/sunrpc/clnt.c | 1 + net/sunrpc/xprt.c | 12 +++++++----- 3 files changed, 9 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 5fea0fb420df..9784e2875e7e 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -324,6 +324,7 @@ struct xprt_class { struct rpc_xprt *xprt_create_transport(struct xprt_create *args); void xprt_connect(struct rpc_task *task); void xprt_reserve(struct rpc_task *task); +void xprt_request_init(struct rpc_task *task); void xprt_retry_reserve(struct rpc_task *task); int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task); int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task); diff --git a/net/sunrpc/clnt.c b/net/sunrpc/clnt.c index c2266f387213..d839c33ae7d9 100644 --- a/net/sunrpc/clnt.c +++ b/net/sunrpc/clnt.c @@ -1546,6 +1546,7 @@ call_reserveresult(struct rpc_task *task) task->tk_status = 0; if (status >= 0) { if (task->tk_rqstp) { + xprt_request_init(task); task->tk_action = call_refresh; return; } diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 70f005044f06..2d959268d9b7 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -66,7 +66,7 @@ * Local functions */ static void xprt_init(struct rpc_xprt *xprt, struct net *net); -static void xprt_request_init(struct rpc_task *, struct rpc_xprt *); +static __be32 xprt_alloc_xid(struct rpc_xprt *xprt); static void xprt_connect_status(struct rpc_task *task); static int __xprt_get_cong(struct rpc_xprt *, struct rpc_task *); static void __xprt_put_cong(struct rpc_xprt *, struct rpc_rqst *); @@ -987,6 +987,8 @@ bool xprt_prepare_transmit(struct rpc_task *task) task->tk_status = -EAGAIN; goto out_unlock; } + if (!bc_prealloc(req) && !req->rq_xmit_bytes_sent) + req->rq_xid = xprt_alloc_xid(xprt); ret = true; out_unlock: spin_unlock_bh(&xprt->transport_lock); @@ -1163,10 +1165,10 @@ void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) out_init_req: xprt->stat.max_slots = max_t(unsigned int, xprt->stat.max_slots, xprt->num_reqs); + spin_unlock(&xprt->reserve_lock); + task->tk_status = 0; task->tk_rqstp = req; - xprt_request_init(task, xprt); - spin_unlock(&xprt->reserve_lock); } EXPORT_SYMBOL_GPL(xprt_alloc_slot); @@ -1303,8 +1305,9 @@ static inline void xprt_init_xid(struct rpc_xprt *xprt) xprt->xid = prandom_u32(); } -static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) +void xprt_request_init(struct rpc_task *task) { + struct rpc_xprt *xprt = task->tk_xprt; struct rpc_rqst *req = task->tk_rqstp; INIT_LIST_HEAD(&req->rq_list); @@ -1312,7 +1315,6 @@ static void xprt_request_init(struct rpc_task *task, struct rpc_xprt *xprt) req->rq_task = task; req->rq_xprt = xprt; req->rq_buffer = NULL; - req->rq_xid = xprt_alloc_xid(xprt); req->rq_connect_cookie = xprt->connect_cookie - 1; req->rq_bytes_sent = 0; req->rq_snd_buf.len = 0; -- cgit From a9cde23ab7cdf5e4e93432dffd0e734267f2b745 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 4 May 2018 15:34:59 -0400 Subject: SUNRPC: Add a ->free_slot transport callout Refactor: xprtrdma needs to have better control over when RPCs are awoken from the backlog queue, so replace xprt_free_slot with a transport op callout. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/xprt.h | 4 ++++ net/sunrpc/xprt.c | 5 +++-- net/sunrpc/xprtrdma/svc_rdma_backchannel.c | 1 + net/sunrpc/xprtrdma/transport.c | 1 + net/sunrpc/xprtsock.c | 4 ++++ 5 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 9784e2875e7e..706eef12bbc0 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -127,6 +127,8 @@ struct rpc_xprt_ops { int (*reserve_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); void (*release_xprt)(struct rpc_xprt *xprt, struct rpc_task *task); void (*alloc_slot)(struct rpc_xprt *xprt, struct rpc_task *task); + void (*free_slot)(struct rpc_xprt *xprt, + struct rpc_rqst *req); void (*rpcbind)(struct rpc_task *task); void (*set_port)(struct rpc_xprt *xprt, unsigned short port); void (*connect)(struct rpc_xprt *xprt, struct rpc_task *task); @@ -329,6 +331,8 @@ void xprt_retry_reserve(struct rpc_task *task); int xprt_reserve_xprt(struct rpc_xprt *xprt, struct rpc_task *task); int xprt_reserve_xprt_cong(struct rpc_xprt *xprt, struct rpc_task *task); void xprt_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task); +void xprt_free_slot(struct rpc_xprt *xprt, + struct rpc_rqst *req); void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task); bool xprt_prepare_transmit(struct rpc_task *task); void xprt_transmit(struct rpc_task *task); diff --git a/net/sunrpc/xprt.c b/net/sunrpc/xprt.c index 2d959268d9b7..3c85af058227 100644 --- a/net/sunrpc/xprt.c +++ b/net/sunrpc/xprt.c @@ -1186,7 +1186,7 @@ void xprt_lock_and_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) } EXPORT_SYMBOL_GPL(xprt_lock_and_alloc_slot); -static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) +void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) { spin_lock(&xprt->reserve_lock); if (!xprt_dynamic_free_slot(xprt, req)) { @@ -1196,6 +1196,7 @@ static void xprt_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *req) xprt_wake_up_backlog(xprt); spin_unlock(&xprt->reserve_lock); } +EXPORT_SYMBOL_GPL(xprt_free_slot); static void xprt_free_all_slots(struct rpc_xprt *xprt) { @@ -1375,7 +1376,7 @@ void xprt_release(struct rpc_task *task) dprintk("RPC: %5u release request %p\n", task->tk_pid, req); if (likely(!bc_prealloc(req))) - xprt_free_slot(xprt, req); + xprt->ops->free_slot(xprt, req); else xprt_free_bc_request(req); } diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c index a73632ca9048..1035516d54e2 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c +++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c @@ -273,6 +273,7 @@ static const struct rpc_xprt_ops xprt_rdma_bc_procs = { .reserve_xprt = xprt_reserve_xprt_cong, .release_xprt = xprt_release_xprt_cong, .alloc_slot = xprt_alloc_slot, + .free_slot = xprt_free_slot, .release_request = xprt_release_rqst_cong, .buf_alloc = xprt_rdma_bc_allocate, .buf_free = xprt_rdma_bc_free, diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 4717578b4b81..cf5e866ee969 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -781,6 +781,7 @@ static const struct rpc_xprt_ops xprt_rdma_procs = { .reserve_xprt = xprt_reserve_xprt_cong, .release_xprt = xprt_release_xprt_cong, /* sunrpc/xprt.c */ .alloc_slot = xprt_alloc_slot, + .free_slot = xprt_free_slot, .release_request = xprt_release_rqst_cong, /* ditto */ .set_retrans_timeout = xprt_set_retrans_timeout_def, /* ditto */ .timer = xprt_rdma_timer, diff --git a/net/sunrpc/xprtsock.c b/net/sunrpc/xprtsock.c index c8902f11efdd..9e1c5024aba9 100644 --- a/net/sunrpc/xprtsock.c +++ b/net/sunrpc/xprtsock.c @@ -2763,6 +2763,7 @@ static const struct rpc_xprt_ops xs_local_ops = { .reserve_xprt = xprt_reserve_xprt, .release_xprt = xs_tcp_release_xprt, .alloc_slot = xprt_alloc_slot, + .free_slot = xprt_free_slot, .rpcbind = xs_local_rpcbind, .set_port = xs_local_set_port, .connect = xs_local_connect, @@ -2782,6 +2783,7 @@ static const struct rpc_xprt_ops xs_udp_ops = { .reserve_xprt = xprt_reserve_xprt_cong, .release_xprt = xprt_release_xprt_cong, .alloc_slot = xprt_alloc_slot, + .free_slot = xprt_free_slot, .rpcbind = rpcb_getport_async, .set_port = xs_set_port, .connect = xs_connect, @@ -2803,6 +2805,7 @@ static const struct rpc_xprt_ops xs_tcp_ops = { .reserve_xprt = xprt_reserve_xprt, .release_xprt = xs_tcp_release_xprt, .alloc_slot = xprt_lock_and_alloc_slot, + .free_slot = xprt_free_slot, .rpcbind = rpcb_getport_async, .set_port = xs_set_port, .connect = xs_connect, @@ -2834,6 +2837,7 @@ static const struct rpc_xprt_ops bc_tcp_ops = { .reserve_xprt = xprt_reserve_xprt, .release_xprt = xprt_release_xprt, .alloc_slot = xprt_alloc_slot, + .free_slot = xprt_free_slot, .buf_alloc = bc_malloc, .buf_free = bc_free, .send_request = bc_send_request, -- cgit From edb41e61a54ee75fae31302775e0301fdcb0caaa Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Fri, 4 May 2018 15:35:09 -0400 Subject: xprtrdma: Make rpc_rqst part of rpcrdma_req This simplifies allocation of the generic RPC slot and xprtrdma specific per-RPC resources. It also makes xprtrdma more like the socket-based transports: ->buf_alloc and ->buf_free are now responsible only for send and receive buffers. Signed-off-by: Chuck Lever Signed-off-by: Anna Schumaker --- include/linux/sunrpc/xprt.h | 1 - net/sunrpc/xprtrdma/backchannel.c | 77 ++++++++++++++++++--------------------- net/sunrpc/xprtrdma/transport.c | 35 +++++------------- net/sunrpc/xprtrdma/xprt_rdma.h | 9 +---- 4 files changed, 46 insertions(+), 76 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/xprt.h b/include/linux/sunrpc/xprt.h index 706eef12bbc0..336fd1a19cca 100644 --- a/include/linux/sunrpc/xprt.h +++ b/include/linux/sunrpc/xprt.h @@ -84,7 +84,6 @@ struct rpc_rqst { void (*rq_release_snd_buf)(struct rpc_rqst *); /* release rq_enc_pages */ struct list_head rq_list; - void *rq_xprtdata; /* Per-xprt private data */ void *rq_buffer; /* Call XDR encode buffer */ size_t rq_callsize; void *rq_rbuffer; /* Reply XDR decode buffer */ diff --git a/net/sunrpc/xprtrdma/backchannel.c b/net/sunrpc/xprtrdma/backchannel.c index 47ebac949769..4034788c9856 100644 --- a/net/sunrpc/xprtrdma/backchannel.c +++ b/net/sunrpc/xprtrdma/backchannel.c @@ -29,29 +29,41 @@ static void rpcrdma_bc_free_rqst(struct rpcrdma_xprt *r_xprt, spin_unlock(&buf->rb_reqslock); rpcrdma_destroy_req(req); - - kfree(rqst); } -static int rpcrdma_bc_setup_rqst(struct rpcrdma_xprt *r_xprt, - struct rpc_rqst *rqst) +static int rpcrdma_bc_setup_reqs(struct rpcrdma_xprt *r_xprt, + unsigned int count) { - struct rpcrdma_regbuf *rb; - struct rpcrdma_req *req; - size_t size; + struct rpc_xprt *xprt = &r_xprt->rx_xprt; + struct rpc_rqst *rqst; + unsigned int i; + + for (i = 0; i < (count << 1); i++) { + struct rpcrdma_regbuf *rb; + struct rpcrdma_req *req; + size_t size; + + req = rpcrdma_create_req(r_xprt); + if (IS_ERR(req)) + return PTR_ERR(req); + rqst = &req->rl_slot; + + rqst->rq_xprt = xprt; + INIT_LIST_HEAD(&rqst->rq_list); + INIT_LIST_HEAD(&rqst->rq_bc_list); + __set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state); + spin_lock_bh(&xprt->bc_pa_lock); + list_add(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); + spin_unlock_bh(&xprt->bc_pa_lock); - req = rpcrdma_create_req(r_xprt); - if (IS_ERR(req)) - return PTR_ERR(req); - - size = r_xprt->rx_data.inline_rsize; - rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, GFP_KERNEL); - if (IS_ERR(rb)) - goto out_fail; - req->rl_sendbuf = rb; - xdr_buf_init(&rqst->rq_snd_buf, rb->rg_base, - min_t(size_t, size, PAGE_SIZE)); - rpcrdma_set_xprtdata(rqst, req); + size = r_xprt->rx_data.inline_rsize; + rb = rpcrdma_alloc_regbuf(size, DMA_TO_DEVICE, GFP_KERNEL); + if (IS_ERR(rb)) + goto out_fail; + req->rl_sendbuf = rb; + xdr_buf_init(&rqst->rq_snd_buf, rb->rg_base, + min_t(size_t, size, PAGE_SIZE)); + } return 0; out_fail: @@ -86,9 +98,6 @@ static int rpcrdma_bc_setup_reps(struct rpcrdma_xprt *r_xprt, int xprt_rdma_bc_setup(struct rpc_xprt *xprt, unsigned int reqs) { struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt); - struct rpcrdma_buffer *buffer = &r_xprt->rx_buf; - struct rpc_rqst *rqst; - unsigned int i; int rc; /* The backchannel reply path returns each rpc_rqst to the @@ -103,25 +112,9 @@ int xprt_rdma_bc_setup(struct rpc_xprt *xprt, unsigned int reqs) if (reqs > RPCRDMA_BACKWARD_WRS >> 1) goto out_err; - for (i = 0; i < (reqs << 1); i++) { - rqst = kzalloc(sizeof(*rqst), GFP_KERNEL); - if (!rqst) - goto out_free; - - dprintk("RPC: %s: new rqst %p\n", __func__, rqst); - - rqst->rq_xprt = &r_xprt->rx_xprt; - INIT_LIST_HEAD(&rqst->rq_list); - INIT_LIST_HEAD(&rqst->rq_bc_list); - __set_bit(RPC_BC_PA_IN_USE, &rqst->rq_bc_pa_state); - - if (rpcrdma_bc_setup_rqst(r_xprt, rqst)) - goto out_free; - - spin_lock_bh(&xprt->bc_pa_lock); - list_add(&rqst->rq_bc_pa_list, &xprt->bc_pa_list); - spin_unlock_bh(&xprt->bc_pa_lock); - } + rc = rpcrdma_bc_setup_reqs(r_xprt, reqs); + if (rc) + goto out_free; rc = rpcrdma_bc_setup_reps(r_xprt, reqs); if (rc) @@ -131,7 +124,7 @@ int xprt_rdma_bc_setup(struct rpc_xprt *xprt, unsigned int reqs) if (rc) goto out_free; - buffer->rb_bc_srv_max_requests = reqs; + r_xprt->rx_buf.rb_bc_srv_max_requests = reqs; request_module("svcrdma"); trace_xprtrdma_cb_setup(r_xprt, reqs); return 0; diff --git a/net/sunrpc/xprtrdma/transport.c b/net/sunrpc/xprtrdma/transport.c index 8f9338e98c4f..79885aa39c24 100644 --- a/net/sunrpc/xprtrdma/transport.c +++ b/net/sunrpc/xprtrdma/transport.c @@ -331,9 +331,7 @@ xprt_setup_rdma(struct xprt_create *args) return ERR_PTR(-EBADF); } - xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt), - xprt_rdma_slot_table_entries, - xprt_rdma_slot_table_entries); + xprt = xprt_alloc(args->net, sizeof(struct rpcrdma_xprt), 0, 0); if (xprt == NULL) { dprintk("RPC: %s: couldn't allocate rpcrdma_xprt\n", __func__); @@ -365,7 +363,7 @@ xprt_setup_rdma(struct xprt_create *args) xprt_set_bound(xprt); xprt_rdma_format_addresses(xprt, sap); - cdata.max_requests = xprt->max_reqs; + cdata.max_requests = xprt_rdma_slot_table_entries; cdata.rsize = RPCRDMA_MAX_SEGS * PAGE_SIZE; /* RDMA write max */ cdata.wsize = RPCRDMA_MAX_SEGS * PAGE_SIZE; /* RDMA read max */ @@ -550,22 +548,18 @@ xprt_rdma_connect(struct rpc_xprt *xprt, struct rpc_task *task) static void xprt_rdma_alloc_slot(struct rpc_xprt *xprt, struct rpc_task *task) { - struct rpc_rqst *rqst; + struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt); + struct rpcrdma_req *req; - spin_lock(&xprt->reserve_lock); - if (list_empty(&xprt->free)) + req = rpcrdma_buffer_get(&r_xprt->rx_buf); + if (!req) goto out_sleep; - rqst = list_first_entry(&xprt->free, struct rpc_rqst, rq_list); - list_del(&rqst->rq_list); - spin_unlock(&xprt->reserve_lock); - - task->tk_rqstp = rqst; + task->tk_rqstp = &req->rl_slot; task->tk_status = 0; return; out_sleep: rpc_sleep_on(&xprt->backlog, task, NULL); - spin_unlock(&xprt->reserve_lock); task->tk_status = -EAGAIN; } @@ -579,11 +573,8 @@ static void xprt_rdma_free_slot(struct rpc_xprt *xprt, struct rpc_rqst *rqst) { memset(rqst, 0, sizeof(*rqst)); - - spin_lock(&xprt->reserve_lock); - list_add(&rqst->rq_list, &xprt->free); + rpcrdma_buffer_put(rpcr_to_rdmar(rqst)); rpc_wake_up_next(&xprt->backlog); - spin_unlock(&xprt->reserve_lock); } static bool @@ -656,13 +647,9 @@ xprt_rdma_allocate(struct rpc_task *task) { struct rpc_rqst *rqst = task->tk_rqstp; struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(rqst->rq_xprt); - struct rpcrdma_req *req; + struct rpcrdma_req *req = rpcr_to_rdmar(rqst); gfp_t flags; - req = rpcrdma_buffer_get(&r_xprt->rx_buf); - if (req == NULL) - goto out_get; - flags = RPCRDMA_DEF_GFP; if (RPC_IS_SWAPPER(task)) flags = __GFP_MEMALLOC | GFP_NOWAIT | __GFP_NOWARN; @@ -672,15 +659,12 @@ xprt_rdma_allocate(struct rpc_task *task) if (!rpcrdma_get_recvbuf(r_xprt, req, rqst->rq_rcvsize, flags)) goto out_fail; - rpcrdma_set_xprtdata(rqst, req); rqst->rq_buffer = req->rl_sendbuf->rg_base; rqst->rq_rbuffer = req->rl_recvbuf->rg_base; trace_xprtrdma_allocate(task, req); return 0; out_fail: - rpcrdma_buffer_put(req); -out_get: trace_xprtrdma_allocate(task, NULL); return -ENOMEM; } @@ -701,7 +685,6 @@ xprt_rdma_free(struct rpc_task *task) if (test_bit(RPCRDMA_REQ_F_PENDING, &req->rl_flags)) rpcrdma_release_rqst(r_xprt, req); trace_xprtrdma_rpc_done(task, req); - rpcrdma_buffer_put(req); } /** diff --git a/net/sunrpc/xprtrdma/xprt_rdma.h b/net/sunrpc/xprtrdma/xprt_rdma.h index 3490a87bc69a..1d7bb6e5db18 100644 --- a/net/sunrpc/xprtrdma/xprt_rdma.h +++ b/net/sunrpc/xprtrdma/xprt_rdma.h @@ -335,6 +335,7 @@ enum { struct rpcrdma_buffer; struct rpcrdma_req { struct list_head rl_list; + struct rpc_rqst rl_slot; struct rpcrdma_buffer *rl_buffer; struct rpcrdma_rep *rl_reply; struct xdr_stream rl_stream; @@ -357,16 +358,10 @@ enum { RPCRDMA_REQ_F_TX_RESOURCES, }; -static inline void -rpcrdma_set_xprtdata(struct rpc_rqst *rqst, struct rpcrdma_req *req) -{ - rqst->rq_xprtdata = req; -} - static inline struct rpcrdma_req * rpcr_to_rdmar(const struct rpc_rqst *rqst) { - return rqst->rq_xprtdata; + return container_of(rqst, struct rpcrdma_req, rl_slot); } static inline void -- cgit From 04e7b3d7eabefa0807b398233f019f274945ee79 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 May 2018 06:35:51 -0300 Subject: iio: iio.h: use nested struct support on kernel-doc markup Solve those Sphinx warnings: ./include/linux/iio/iio.h:270: warning: Function parameter or member 'scan_type.sign' not described in 'iio_chan_spec' ./include/linux/iio/iio.h:270: warning: Function parameter or member 'scan_type.realbits' not described in 'iio_chan_spec' ./include/linux/iio/iio.h:270: warning: Function parameter or member 'scan_type.storagebits' not described in 'iio_chan_spec' ./include/linux/iio/iio.h:270: warning: Function parameter or member 'scan_type.shift' not described in 'iio_chan_spec' ./include/linux/iio/iio.h:270: warning: Function parameter or member 'scan_type.repeat' not described in 'iio_chan_spec' ./include/linux/iio/iio.h:270: warning: Function parameter or member 'scan_type.endianness' not described in 'iio_chan_spec' ./include/linux/iio/iio.h:191: WARNING: Unexpected indentation. ./include/linux/iio/iio.h:192: WARNING: Block quote ends without a blank line; unexpected unindent. ./include/linux/iio/iio.h:198: WARNING: Definition list ends without a blank line; unexpected unindent. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 11579fd4126e..a74cb177dc6f 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -183,18 +183,18 @@ struct iio_event_spec { * @address: Driver specific identifier. * @scan_index: Monotonic index to give ordering in scans when read * from a buffer. - * @scan_type: sign: 's' or 'u' to specify signed or unsigned - * realbits: Number of valid bits of data - * storagebits: Realbits + padding - * shift: Shift right by this before masking out - * realbits. - * repeat: Number of times real/storage bits - * repeats. When the repeat element is - * more than 1, then the type element in - * sysfs will show a repeat value. - * Otherwise, the number of repetitions is - * omitted. - * endianness: little or big endian + * @scan_type: struct describing the scan type + * @scan_type.sign: 's' or 'u' to specify signed or unsigned + * @scan_type.realbits: Number of valid bits of data + * @scan_type.storagebits: Realbits + padding + * @scan_type.shift: Shift right by this before masking out + * realbits. + * @scan_type.repeat: Number of times real/storage bits repeats. + * When the repeat element is more than 1, then + * the type element in sysfs will show a repeat + * value. Otherwise, the number of repetitions + * is omitted. + * @scan_type.endianness: little or big endian * @info_mask_separate: What information is to be exported that is specific to * this channel. * @info_mask_separate_available: What availability information is to be -- cgit From a676688032e819c4bb1999a422553a1a1f52ce45 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 7 May 2018 06:35:52 -0300 Subject: mtd: rawnand.h: use nested union kernel-doc markups Gets rid of those warnings and better document the parameters. ./include/linux/mtd/rawnand.h:752: warning: Function parameter or member 'timings.sdr' not described in 'nand_data_interface' ./include/linux/mtd/rawnand.h:817: warning: Function parameter or member 'buf' not described in 'nand_op_data_instr' ./include/linux/mtd/rawnand.h:817: warning: Function parameter or member 'buf.in' not described in 'nand_op_data_instr' ./include/linux/mtd/rawnand.h:817: warning: Function parameter or member 'buf.out' not described in 'nand_op_data_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.cmd' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.addr' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.data' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:863: warning: Function parameter or member 'ctx.waitrdy' not described in 'nand_op_instr' ./include/linux/mtd/rawnand.h:1010: warning: Function parameter or member 'ctx' not described in 'nand_op_parser_pattern_elem' ./include/linux/mtd/rawnand.h:1010: warning: Function parameter or member 'ctx.addr' not described in 'nand_op_parser_pattern_elem' ./include/linux/mtd/rawnand.h:1010: warning: Function parameter or member 'ctx.data' not described in 'nand_op_parser_pattern_elem' ./include/linux/mtd/rawnand.h:1313: warning: Function parameter or member 'manufacturer.desc' not described in 'nand_chip' ./include/linux/mtd/rawnand.h:1313: warning: Function parameter or member 'manufacturer.priv' not described in 'nand_chip' ./include/linux/mtd/rawnand.h:848: WARNING: Unexpected indentation. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Boris Brezillon --- include/linux/mtd/rawnand.h | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mtd/rawnand.h b/include/linux/mtd/rawnand.h index ba8d908f5cc7..a206172ec23b 100644 --- a/include/linux/mtd/rawnand.h +++ b/include/linux/mtd/rawnand.h @@ -747,8 +747,9 @@ enum nand_data_interface_type { /** * struct nand_data_interface - NAND interface timing - * @type: type of the timing - * @timings: The timing, type according to @type + * @type: type of the timing + * @timings: The timing, type according to @type + * @timings.sdr: Use it when @type is %NAND_SDR_IFACE. */ struct nand_data_interface { enum nand_data_interface_type type; @@ -805,8 +806,9 @@ struct nand_op_addr_instr { /** * struct nand_op_data_instr - Definition of a data instruction * @len: number of data bytes to move - * @in: buffer to fill when reading from the NAND chip - * @out: buffer to read from when writing to the NAND chip + * @buf: buffer to fill + * @buf.in: buffer to fill when reading from the NAND chip + * @buf.out: buffer to read from when writing to the NAND chip * @force_8bit: force 8-bit access * * Please note that "in" and "out" are inverted from the ONFI specification @@ -849,9 +851,13 @@ enum nand_op_instr_type { /** * struct nand_op_instr - Instruction object * @type: the instruction type - * @cmd/@addr/@data/@waitrdy: extra data associated to the instruction. - * You'll have to use the appropriate element - * depending on @type + * @ctx: extra data associated to the instruction. You'll have to use the + * appropriate element depending on @type + * @ctx.cmd: use it if @type is %NAND_OP_CMD_INSTR + * @ctx.addr: use it if @type is %NAND_OP_ADDR_INSTR + * @ctx.data: use it if @type is %NAND_OP_DATA_IN_INSTR + * or %NAND_OP_DATA_OUT_INSTR + * @ctx.waitrdy: use it if @type is %NAND_OP_WAITRDY_INSTR * @delay_ns: delay the controller should apply after the instruction has been * issued on the bus. Most modern controllers have internal timings * control logic, and in this case, the controller driver can ignore @@ -1004,7 +1010,9 @@ struct nand_op_parser_data_constraints { * struct nand_op_parser_pattern_elem - One element of a pattern * @type: the instructuction type * @optional: whether this element of the pattern is optional or mandatory - * @addr/@data: address or data constraint (number of cycles or data length) + * @ctx: address or data constraint + * @ctx.addr: address constraint (number of cycles) + * @ctx.data: data constraint (data length) */ struct nand_op_parser_pattern_elem { enum nand_op_instr_type type; @@ -1231,6 +1239,8 @@ int nand_op_parser_exec_op(struct nand_chip *chip, * devices. * @priv: [OPTIONAL] pointer to private chip data * @manufacturer: [INTERN] Contains manufacturer information + * @manufacturer.desc: [INTERN] Contains manufacturer's description + * @manufacturer.priv: [INTERN] Contains manufacturer private information */ struct nand_chip { -- cgit From 1baf47c2e5c946fd17ef07597b9d25722d13ff14 Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 9 May 2018 17:52:06 +0100 Subject: firmware: arm_scmi: fix kernel-docs documentation There are few missing descriptions for function parameters and structure members along with certain instances where excessive function parameters or structure members are described. This patch fixes all of those warnings. Reported-by: Jonathan Cameron Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/base.c | 20 +++++++++-------- drivers/firmware/arm_scmi/common.h | 7 ++++-- drivers/firmware/arm_scmi/driver.c | 45 ++++++++++++++++++++------------------ include/linux/scmi_protocol.h | 8 +++++++ 4 files changed, 48 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/drivers/firmware/arm_scmi/base.c b/drivers/firmware/arm_scmi/base.c index 0d3806c0d432..c36ded9dbb83 100644 --- a/drivers/firmware/arm_scmi/base.c +++ b/drivers/firmware/arm_scmi/base.c @@ -26,7 +26,7 @@ struct scmi_msg_resp_base_attributes { * scmi_base_attributes_get() - gets the implementation details * that are associated with the base protocol. * - * @handle - SCMI entity handle + * @handle: SCMI entity handle * * Return: 0 on success, else appropriate SCMI error. */ @@ -50,14 +50,15 @@ static int scmi_base_attributes_get(const struct scmi_handle *handle) } scmi_one_xfer_put(handle, t); + return ret; } /** * scmi_base_vendor_id_get() - gets vendor/subvendor identifier ASCII string. * - * @handle - SCMI entity handle - * @sub_vendor - specify true if sub-vendor ID is needed + * @handle: SCMI entity handle + * @sub_vendor: specify true if sub-vendor ID is needed * * Return: 0 on success, else appropriate SCMI error. */ @@ -97,7 +98,7 @@ scmi_base_vendor_id_get(const struct scmi_handle *handle, bool sub_vendor) * implementation 32-bit version. The format of the version number is * vendor-specific * - * @handle - SCMI entity handle + * @handle: SCMI entity handle * * Return: 0 on success, else appropriate SCMI error. */ @@ -128,8 +129,8 @@ scmi_base_implementation_version_get(const struct scmi_handle *handle) * scmi_base_implementation_list_get() - gets the list of protocols it is * OSPM is allowed to access * - * @handle - SCMI entity handle - * @protocols_imp - pointer to hold the list of protocol identifiers + * @handle: SCMI entity handle + * @protocols_imp: pointer to hold the list of protocol identifiers * * Return: 0 on success, else appropriate SCMI error. */ @@ -173,15 +174,16 @@ static int scmi_base_implementation_list_get(const struct scmi_handle *handle, } while (loop_num_ret); scmi_one_xfer_put(handle, t); + return ret; } /** * scmi_base_discover_agent_get() - discover the name of an agent * - * @handle - SCMI entity handle - * @id - Agent identifier - * @name - Agent identifier ASCII string + * @handle: SCMI entity handle + * @id: Agent identifier + * @name: Agent identifier ASCII string * * An agent id of 0 is reserved to identify the platform itself. * Generally operating system is represented as "OSPM" diff --git a/drivers/firmware/arm_scmi/common.h b/drivers/firmware/arm_scmi/common.h index e8f332c9c469..0821662a4633 100644 --- a/drivers/firmware/arm_scmi/common.h +++ b/drivers/firmware/arm_scmi/common.h @@ -51,8 +51,11 @@ struct scmi_msg_resp_prot_version { * @id: The identifier of the command being sent * @protocol_id: The identifier of the protocol used to send @id command * @seq: The token to identify the message. when a message/command returns, - * the platform returns the whole message header unmodified including - * the token. + * the platform returns the whole message header unmodified including + * the token + * @status: Status of the transfer once it's complete + * @poll_completion: Indicate if the transfer needs to be polled for + * completion or interrupt mode is used */ struct scmi_msg_hdr { u8 id; diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 917786d91f55..6fee11f06a66 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -51,7 +51,7 @@ enum scmi_error_codes { SCMI_ERR_MAX }; -/* List of all SCMI devices active in system */ +/* List of all SCMI devices active in system */ static LIST_HEAD(scmi_list); /* Protection for the entire list */ static DEFINE_MUTEX(scmi_list_mutex); @@ -68,7 +68,6 @@ static DEFINE_MUTEX(scmi_list_mutex); struct scmi_xfers_info { struct scmi_xfer *xfer_block; unsigned long *xfer_alloc_table; - /* protect transfer allocation */ spinlock_t xfer_lock; }; @@ -94,6 +93,7 @@ struct scmi_desc { * @payload: Transmit/Receive mailbox channel payload area * @dev: Reference to device in the SCMI hierarchy corresponding to this * channel + * @handle: Pointer to SCMI entity handle */ struct scmi_chan_info { struct mbox_client cl; @@ -104,7 +104,7 @@ struct scmi_chan_info { }; /** - * struct scmi_info - Structure representing a SCMI instance + * struct scmi_info - Structure representing a SCMI instance * * @dev: Device pointer * @desc: SoC description for this instance @@ -113,9 +113,9 @@ struct scmi_chan_info { * implementation version and (sub-)vendor identification. * @minfo: Message info * @tx_idr: IDR object to map protocol id to channel info pointer - * @protocols_imp: list of protocols implemented, currently maximum of + * @protocols_imp: List of protocols implemented, currently maximum of * MAX_PROTOCOLS_IMP elements allocated by the base protocol - * @node: list head + * @node: List head * @users: Number of users of this instance */ struct scmi_info { @@ -221,9 +221,7 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) xfer_id = MSG_XTRACT_TOKEN(ioread32(&mem->msg_header)); - /* - * Are we even expecting this? - */ + /* Are we even expecting this? */ if (!test_bit(xfer_id, minfo->xfer_alloc_table)) { dev_err(dev, "message for %d is not expected!\n", xfer_id); return; @@ -248,6 +246,8 @@ static void scmi_rx_callback(struct mbox_client *cl, void *m) * * @hdr: pointer to header containing all the information on message id, * protocol id and sequence id. + * + * Return: 32-bit packed command header to be sent to the platform. */ static inline u32 pack_scmi_header(struct scmi_msg_hdr *hdr) { @@ -282,9 +282,9 @@ static void scmi_tx_prepare(struct mbox_client *cl, void *m) } /** - * scmi_one_xfer_get() - Allocate one message + * scmi_xfer_get() - Allocate one message * - * @handle: SCMI entity handle + * @handle: Pointer to SCMI entity handle * * Helper function which is used by various command functions that are * exposed to clients of this driver for allocating a message traffic event. @@ -326,8 +326,8 @@ static struct scmi_xfer *scmi_one_xfer_get(const struct scmi_handle *handle) /** * scmi_one_xfer_put() - Release a message * - * @minfo: transfer info pointer - * @xfer: message that was reserved by scmi_one_xfer_get + * @handle: Pointer to SCMI entity handle + * @xfer: message that was reserved by scmi_xfer_get * * This holds a spinlock to maintain integrity of internal data structures. */ @@ -374,12 +374,12 @@ static bool scmi_xfer_done_no_timeout(const struct scmi_chan_info *cinfo, /** * scmi_do_xfer() - Do one transfer * - * @info: Pointer to SCMI entity information + * @handle: Pointer to SCMI entity handle * @xfer: Transfer to initiate and wait for response * * Return: -ETIMEDOUT in case of no response, if transmit error, - * return corresponding error, else if all goes well, - * return 0. + * return corresponding error, else if all goes well, + * return 0. */ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) { @@ -438,9 +438,9 @@ int scmi_do_xfer(const struct scmi_handle *handle, struct scmi_xfer *xfer) /** * scmi_one_xfer_init() - Allocate and initialise one message * - * @handle: SCMI entity handle + * @handle: Pointer to SCMI entity handle * @msg_id: Message identifier - * @msg_prot_id: Protocol identifier for the message + * @prot_id: Protocol identifier for the message * @tx_size: transmit message size * @rx_size: receive message size * @p: pointer to the allocated and initialised message @@ -478,13 +478,16 @@ int scmi_one_xfer_init(const struct scmi_handle *handle, u8 msg_id, u8 prot_id, xfer->hdr.poll_completion = false; *p = xfer; + return 0; } /** * scmi_version_get() - command to get the revision of the SCMI entity * - * @handle: Handle to SCMI entity information + * @handle: Pointer to SCMI entity handle + * @protocol: Protocol identifier for the message + * @version: Holds returned version of protocol. * * Updates the SCMI information in the internal data structure. * @@ -541,7 +544,7 @@ scmi_is_protocol_implemented(const struct scmi_handle *handle, u8 prot_id) * @dev: pointer to device for which we want SCMI handle * * NOTE: The function does not track individual clients of the framework - * and is expected to be maintained by caller of SCMI protocol library. + * and is expected to be maintained by caller of SCMI protocol library. * scmi_handle_put must be balanced with successful scmi_handle_get * * Return: pointer to handle if successful, NULL on error @@ -572,7 +575,7 @@ struct scmi_handle *scmi_handle_get(struct device *dev) * @handle: handle acquired by scmi_handle_get * * NOTE: The function does not track individual clients of the framework - * and is expected to be maintained by caller of SCMI protocol library. + * and is expected to be maintained by caller of SCMI protocol library. * scmi_handle_put must be balanced with successful scmi_handle_get * * Return: 0 is successfully released @@ -595,7 +598,7 @@ int scmi_handle_put(const struct scmi_handle *handle) } static const struct scmi_desc scmi_generic_desc = { - .max_rx_timeout_ms = 30, /* we may increase this if required */ + .max_rx_timeout_ms = 30, /* We may increase this if required */ .max_msg = 20, /* Limited by MBOX_TX_QUEUE_LEN */ .max_msg_size = 128, }; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index b458c87b866c..a171c1e293e8 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -189,6 +189,14 @@ struct scmi_sensor_ops { * @perf_ops: pointer to set of performance protocol operations * @clk_ops: pointer to set of clock protocol operations * @sensor_ops: pointer to set of sensor protocol operations + * @perf_priv: pointer to private data structure specific to performance + * protocol(for internal use only) + * @clk_priv: pointer to private data structure specific to clock + * protocol(for internal use only) + * @power_priv: pointer to private data structure specific to power + * protocol(for internal use only) + * @sensor_priv: pointer to private data structure specific to sensors + * protocol(for internal use only) */ struct scmi_handle { struct device *dev; -- cgit From 826950868cd81268913a91b69f00fb6c3f0b6f5b Mon Sep 17 00:00:00 2001 From: Andrzej Hajda Date: Fri, 27 Apr 2018 14:16:38 +0200 Subject: remoteproc/ste: remove abandoned include file STE modem driver has been removed in 2016. This include has no users since then. Signed-off-by: Andrzej Hajda Cc: Jean Delvare Cc: Linus Walleij Cc: Ohad Ben-Cohen Cc: Bjorn Andersson Cc: Suman Anna Acked-by: Jean Delvare Acked-by: Linus Walleij Signed-off-by: Bjorn Andersson --- include/linux/ste_modem_shm.h | 56 ------------------------------------------- 1 file changed, 56 deletions(-) delete mode 100644 include/linux/ste_modem_shm.h (limited to 'include/linux') diff --git a/include/linux/ste_modem_shm.h b/include/linux/ste_modem_shm.h deleted file mode 100644 index 8444a4eff1bb..000000000000 --- a/include/linux/ste_modem_shm.h +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) ST-Ericsson AB 2012 - * Author: Sjur Brendeland / sjur.brandeland@stericsson.com - * - * License terms: GNU General Public License (GPL) version 2 - */ - -#ifndef __INC_MODEM_DEV_H -#define __INC_MODEM_DEV_H -#include -#include - -struct ste_modem_device; - -/** - * struct ste_modem_dev_cb - Callbacks for modem initiated events. - * @kick: Called when the modem kicks the host. - * - * This structure contains callbacks for actions triggered by the modem. - */ -struct ste_modem_dev_cb { - void (*kick)(struct ste_modem_device *mdev, int notify_id); -}; - -/** - * struct ste_modem_dev_ops - Functions to control modem and modem interface. - * - * @power: Main power switch, used for cold-start or complete power off. - * @kick: Kick the modem. - * @kick_subscribe: Subscribe for notifications from the modem. - * @setup: Provide callback functions to modem device. - * - * This structure contains functions used by the ste remoteproc driver - * to manage the modem. - */ -struct ste_modem_dev_ops { - int (*power)(struct ste_modem_device *mdev, bool on); - int (*kick)(struct ste_modem_device *mdev, int notify_id); - int (*kick_subscribe)(struct ste_modem_device *mdev, int notify_id); - int (*setup)(struct ste_modem_device *mdev, - struct ste_modem_dev_cb *cfg); -}; - -/** - * struct ste_modem_device - represent the STE modem device - * @pdev: Reference to platform device - * @ops: Operations used to manage the modem. - * @drv_data: Driver private data. - */ -struct ste_modem_device { - struct platform_device pdev; - struct ste_modem_dev_ops ops; - void *drv_data; -}; - -#endif /*INC_MODEM_DEV_H*/ -- cgit From 7859e08c1bdef00841d29e8ff320264fd6f9257b Mon Sep 17 00:00:00 2001 From: Sudeep Holla Date: Wed, 9 May 2018 17:52:06 +0100 Subject: firmware: arm_scmi: rename get_transition_latency and add_opps_to_device Most of the scmi code follows the suggestion from Greg KH on a totally different thread[0] to have the subsystem name first, followed by the noun and finally the verb with couple of these exceptions. This patch fixes them so that all the functions names are aligned to that practice. [0] https://www.spinics.net/lists/arm-kernel/msg583673.html Acked-by: Rafael J. Wysocki Signed-off-by: Sudeep Holla --- drivers/cpufreq/scmi-cpufreq.c | 4 ++-- drivers/firmware/arm_scmi/perf.c | 10 +++++----- include/linux/scmi_protocol.h | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/cpufreq/scmi-cpufreq.c b/drivers/cpufreq/scmi-cpufreq.c index b4dbc77459b6..50b1551ba894 100644 --- a/drivers/cpufreq/scmi-cpufreq.c +++ b/drivers/cpufreq/scmi-cpufreq.c @@ -117,7 +117,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) return -ENODEV; } - ret = handle->perf_ops->add_opps_to_device(handle, cpu_dev); + ret = handle->perf_ops->device_opps_add(handle, cpu_dev); if (ret) { dev_warn(cpu_dev, "failed to add opps to the device\n"); return ret; @@ -164,7 +164,7 @@ static int scmi_cpufreq_init(struct cpufreq_policy *policy) /* SCMI allows DVFS request for any domain from any CPU */ policy->dvfs_possible_from_any_cpu = true; - latency = handle->perf_ops->get_transition_latency(handle, cpu_dev); + latency = handle->perf_ops->transition_latency_get(handle, cpu_dev); if (!latency) latency = CPUFREQ_ETERNAL; diff --git a/drivers/firmware/arm_scmi/perf.c b/drivers/firmware/arm_scmi/perf.c index 987c64d19801..611ab08e6174 100644 --- a/drivers/firmware/arm_scmi/perf.c +++ b/drivers/firmware/arm_scmi/perf.c @@ -349,8 +349,8 @@ static int scmi_dev_domain_id(struct device *dev) return clkspec.args[0]; } -static int scmi_dvfs_add_opps_to_device(const struct scmi_handle *handle, - struct device *dev) +static int scmi_dvfs_device_opps_add(const struct scmi_handle *handle, + struct device *dev) { int idx, ret, domain; unsigned long freq; @@ -383,7 +383,7 @@ static int scmi_dvfs_add_opps_to_device(const struct scmi_handle *handle, return 0; } -static int scmi_dvfs_get_transition_latency(const struct scmi_handle *handle, +static int scmi_dvfs_transition_latency_get(const struct scmi_handle *handle, struct device *dev) { struct perf_dom_info *dom; @@ -432,8 +432,8 @@ static struct scmi_perf_ops perf_ops = { .level_set = scmi_perf_level_set, .level_get = scmi_perf_level_get, .device_domain_id = scmi_dev_domain_id, - .get_transition_latency = scmi_dvfs_get_transition_latency, - .add_opps_to_device = scmi_dvfs_add_opps_to_device, + .transition_latency_get = scmi_dvfs_transition_latency_get, + .device_opps_add = scmi_dvfs_device_opps_add, .freq_set = scmi_dvfs_freq_set, .freq_get = scmi_dvfs_freq_get, }; diff --git a/include/linux/scmi_protocol.h b/include/linux/scmi_protocol.h index a171c1e293e8..f4c9fc0fc755 100644 --- a/include/linux/scmi_protocol.h +++ b/include/linux/scmi_protocol.h @@ -85,8 +85,8 @@ struct scmi_clk_ops { * @level_set: sets the performance level of a domain * @level_get: gets the performance level of a domain * @device_domain_id: gets the scmi domain id for a given device - * @get_transition_latency: gets the DVFS transition latency for a given device - * @add_opps_to_device: adds all the OPPs for a given device + * @transition_latency_get: gets the DVFS transition latency for a given device + * @device_opps_add: adds all the OPPs for a given device * @freq_set: sets the frequency for a given device using sustained frequency * to sustained performance level mapping * @freq_get: gets the frequency for a given device using sustained frequency @@ -102,10 +102,10 @@ struct scmi_perf_ops { int (*level_get)(const struct scmi_handle *handle, u32 domain, u32 *level, bool poll); int (*device_domain_id)(struct device *dev); - int (*get_transition_latency)(const struct scmi_handle *handle, + int (*transition_latency_get)(const struct scmi_handle *handle, struct device *dev); - int (*add_opps_to_device)(const struct scmi_handle *handle, - struct device *dev); + int (*device_opps_add)(const struct scmi_handle *handle, + struct device *dev); int (*freq_set)(const struct scmi_handle *handle, u32 domain, unsigned long rate, bool poll); int (*freq_get)(const struct scmi_handle *handle, u32 domain, -- cgit From bcf3ffd405df6998914b248d2f22625544a4dd56 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:26:55 -0400 Subject: svcrdma: Add proper SPDX tags for NetApp-contributed source Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma.c | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 1 + net/sunrpc/xprtrdma/svc_rdma_sendto.c | 1 + net/sunrpc/xprtrdma/svc_rdma_transport.c | 1 + 5 files changed, 5 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 7337e1221590..88da0c9bd7b1 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved. * diff --git a/net/sunrpc/xprtrdma/svc_rdma.c b/net/sunrpc/xprtrdma/svc_rdma.c index dd8a431dc2ae..a49053296be4 100644 --- a/net/sunrpc/xprtrdma/svc_rdma.c +++ b/net/sunrpc/xprtrdma/svc_rdma.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved. * diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 3d45015dca97..9eae95de89a7 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2016, 2017 Oracle. All rights reserved. * Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved. diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 649441d5087d..79bd3a394da2 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2016 Oracle. All rights reserved. * Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved. diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 96cc8f6597d3..36332540f8d4 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved. * Copyright (c) 2005-2007 Network Appliance, Inc. All rights reserved. -- cgit From ecf85b2384ea5f7cb0577bf6143bc46d9ecfe4d3 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:27:21 -0400 Subject: svcrdma: Introduce svc_rdma_recv_ctxt svc_rdma_op_ctxt's are pre-allocated and maintained on a per-xprt free list. This eliminates the overhead of calling kmalloc / kfree, both of which grab a globally shared lock that disables interrupts. To reduce contention further, separate the use of these objects in the Receive and Send paths in svcrdma. Subsequent patches will take advantage of this separation by allocating real resources which are then cached in these objects. The allocations are freed when the transport is torn down. I've renamed the structure so that static type checking can be used to ensure that uses of op_ctxt and recv_ctxt are not confused. As an additional clean up, structure fields are renamed to conform with kernel coding conventions. As a final clean up, helpers related to recv_ctxt are moved closer to the functions that use them. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 24 ++- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 318 ++++++++++++++++++++++++++----- net/sunrpc/xprtrdma/svc_rdma_rw.c | 84 ++++---- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 2 +- net/sunrpc/xprtrdma/svc_rdma_transport.c | 142 +------------- 5 files changed, 349 insertions(+), 221 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 88da0c9bd7b1..37f759d65348 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -128,6 +128,9 @@ struct svcxprt_rdma { unsigned long sc_flags; struct list_head sc_read_complete_q; struct work_struct sc_work; + + spinlock_t sc_recv_lock; + struct list_head sc_recv_ctxts; }; /* sc_flags */ #define RDMAXPRT_CONN_PENDING 3 @@ -142,6 +145,19 @@ struct svcxprt_rdma { #define RPCSVC_MAXPAYLOAD_RDMA RPCSVC_MAXPAYLOAD +struct svc_rdma_recv_ctxt { + struct list_head rc_list; + struct ib_recv_wr rc_recv_wr; + struct ib_cqe rc_cqe; + struct xdr_buf rc_arg; + u32 rc_byte_len; + unsigned int rc_page_count; + unsigned int rc_hdr_count; + struct ib_sge rc_sges[1 + + RPCRDMA_MAX_INLINE_THRESH / PAGE_SIZE]; + struct page *rc_pages[RPCSVC_MAXPAGES]; +}; + /* Track DMA maps for this transport and context */ static inline void svc_rdma_count_mappings(struct svcxprt_rdma *rdma, struct svc_rdma_op_ctxt *ctxt) @@ -155,13 +171,19 @@ extern int svc_rdma_handle_bc_reply(struct rpc_xprt *xprt, struct xdr_buf *rcvbuf); /* svc_rdma_recvfrom.c */ +extern void svc_rdma_recv_ctxts_destroy(struct svcxprt_rdma *rdma); +extern bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma); +extern void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, + struct svc_rdma_recv_ctxt *ctxt, + int free_pages); +extern void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma); extern int svc_rdma_recvfrom(struct svc_rqst *); /* svc_rdma_rw.c */ extern void svc_rdma_destroy_rw_ctxts(struct svcxprt_rdma *rdma); extern int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, - struct svc_rdma_op_ctxt *head, __be32 *p); + struct svc_rdma_recv_ctxt *head, __be32 *p); extern int svc_rdma_send_write_chunk(struct svcxprt_rdma *rdma, __be32 *wr_ch, struct xdr_buf *xdr); extern int svc_rdma_send_reply_chunk(struct svcxprt_rdma *rdma, diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 330d542fd96e..b7d9c55ee896 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (c) 2016, 2017 Oracle. All rights reserved. + * Copyright (c) 2016-2018 Oracle. All rights reserved. * Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved. * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved. * @@ -61,7 +61,7 @@ * svc_rdma_recvfrom must post RDMA Reads to pull the RPC Call's * data payload from the client. svc_rdma_recvfrom sets up the * RDMA Reads using pages in svc_rqst::rq_pages, which are - * transferred to an svc_rdma_op_ctxt for the duration of the + * transferred to an svc_rdma_recv_ctxt for the duration of the * I/O. svc_rdma_recvfrom then returns zero, since the RPC message * is still not yet ready. * @@ -70,18 +70,18 @@ * svc_rdma_recvfrom again. This second call may use a different * svc_rqst than the first one, thus any information that needs * to be preserved across these two calls is kept in an - * svc_rdma_op_ctxt. + * svc_rdma_recv_ctxt. * * The second call to svc_rdma_recvfrom performs final assembly * of the RPC Call message, using the RDMA Read sink pages kept in - * the svc_rdma_op_ctxt. The xdr_buf is copied from the - * svc_rdma_op_ctxt to the second svc_rqst. The second call returns + * the svc_rdma_recv_ctxt. The xdr_buf is copied from the + * svc_rdma_recv_ctxt to the second svc_rqst. The second call returns * the length of the completed RPC Call message. * * Page Management * * Pages under I/O must be transferred from the first svc_rqst to an - * svc_rdma_op_ctxt before the first svc_rdma_recvfrom call returns. + * svc_rdma_recv_ctxt before the first svc_rdma_recvfrom call returns. * * The first svc_rqst supplies pages for RDMA Reads. These are moved * from rqstp::rq_pages into ctxt::pages. The consumed elements of @@ -89,7 +89,7 @@ * svc_rdma_recvfrom call returns. * * During the second svc_rdma_recvfrom call, RDMA Read sink pages - * are transferred from the svc_rdma_op_ctxt to the second svc_rqst + * are transferred from the svc_rdma_recv_ctxt to the second svc_rqst * (see rdma_read_complete() below). */ @@ -108,13 +108,247 @@ #define RPCDBG_FACILITY RPCDBG_SVCXPRT +static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc); + +static inline struct svc_rdma_recv_ctxt * +svc_rdma_next_recv_ctxt(struct list_head *list) +{ + return list_first_entry_or_null(list, struct svc_rdma_recv_ctxt, + rc_list); +} + +/** + * svc_rdma_recv_ctxts_destroy - Release all recv_ctxt's for an xprt + * @rdma: svcxprt_rdma being torn down + * + */ +void svc_rdma_recv_ctxts_destroy(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_recv_ctxt *ctxt; + + while ((ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_recv_ctxts))) { + list_del(&ctxt->rc_list); + kfree(ctxt); + } +} + +static struct svc_rdma_recv_ctxt * +svc_rdma_recv_ctxt_get(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_recv_ctxt *ctxt; + + spin_lock(&rdma->sc_recv_lock); + ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_recv_ctxts); + if (!ctxt) + goto out_empty; + list_del(&ctxt->rc_list); + spin_unlock(&rdma->sc_recv_lock); + +out: + ctxt->rc_recv_wr.num_sge = 0; + ctxt->rc_page_count = 0; + return ctxt; + +out_empty: + spin_unlock(&rdma->sc_recv_lock); + + ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return NULL; + goto out; +} + +static void svc_rdma_recv_ctxt_unmap(struct svcxprt_rdma *rdma, + struct svc_rdma_recv_ctxt *ctxt) +{ + struct ib_device *device = rdma->sc_cm_id->device; + int i; + + for (i = 0; i < ctxt->rc_recv_wr.num_sge; i++) + ib_dma_unmap_page(device, + ctxt->rc_sges[i].addr, + ctxt->rc_sges[i].length, + DMA_FROM_DEVICE); +} + +/** + * svc_rdma_recv_ctxt_put - Return recv_ctxt to free list + * @rdma: controlling svcxprt_rdma + * @ctxt: object to return to the free list + * @free_pages: Non-zero if rc_pages should be freed + * + */ +void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, + struct svc_rdma_recv_ctxt *ctxt, + int free_pages) +{ + unsigned int i; + + if (free_pages) + for (i = 0; i < ctxt->rc_page_count; i++) + put_page(ctxt->rc_pages[i]); + spin_lock(&rdma->sc_recv_lock); + list_add(&ctxt->rc_list, &rdma->sc_recv_ctxts); + spin_unlock(&rdma->sc_recv_lock); +} + +static int svc_rdma_post_recv(struct svcxprt_rdma *rdma) +{ + struct ib_device *device = rdma->sc_cm_id->device; + struct svc_rdma_recv_ctxt *ctxt; + struct ib_recv_wr *bad_recv_wr; + int sge_no, buflen, ret; + struct page *page; + dma_addr_t pa; + + ctxt = svc_rdma_recv_ctxt_get(rdma); + if (!ctxt) + return -ENOMEM; + + buflen = 0; + ctxt->rc_cqe.done = svc_rdma_wc_receive; + for (sge_no = 0; buflen < rdma->sc_max_req_size; sge_no++) { + if (sge_no >= rdma->sc_max_sge) { + pr_err("svcrdma: Too many sges (%d)\n", sge_no); + goto err_put_ctxt; + } + + page = alloc_page(GFP_KERNEL); + if (!page) + goto err_put_ctxt; + ctxt->rc_pages[sge_no] = page; + ctxt->rc_page_count++; + + pa = ib_dma_map_page(device, ctxt->rc_pages[sge_no], + 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (ib_dma_mapping_error(device, pa)) + goto err_put_ctxt; + ctxt->rc_sges[sge_no].addr = pa; + ctxt->rc_sges[sge_no].length = PAGE_SIZE; + ctxt->rc_sges[sge_no].lkey = rdma->sc_pd->local_dma_lkey; + ctxt->rc_recv_wr.num_sge++; + + buflen += PAGE_SIZE; + } + ctxt->rc_recv_wr.next = NULL; + ctxt->rc_recv_wr.sg_list = &ctxt->rc_sges[0]; + ctxt->rc_recv_wr.wr_cqe = &ctxt->rc_cqe; + + svc_xprt_get(&rdma->sc_xprt); + ret = ib_post_recv(rdma->sc_qp, &ctxt->rc_recv_wr, &bad_recv_wr); + trace_svcrdma_post_recv(&ctxt->rc_recv_wr, ret); + if (ret) + goto err_post; + return 0; + +err_put_ctxt: + svc_rdma_recv_ctxt_unmap(rdma, ctxt); + svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + return -ENOMEM; +err_post: + svc_rdma_recv_ctxt_unmap(rdma, ctxt); + svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + svc_xprt_put(&rdma->sc_xprt); + return ret; +} + +/** + * svc_rdma_post_recvs - Post initial set of Recv WRs + * @rdma: fresh svcxprt_rdma + * + * Returns true if successful, otherwise false. + */ +bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma) +{ + unsigned int i; + int ret; + + for (i = 0; i < rdma->sc_max_requests; i++) { + ret = svc_rdma_post_recv(rdma); + if (ret) { + pr_err("svcrdma: failure posting recv buffers: %d\n", + ret); + return false; + } + } + return true; +} + +/** + * svc_rdma_wc_receive - Invoked by RDMA provider for each polled Receive WC + * @cq: Completion Queue context + * @wc: Work Completion object + * + * NB: The svc_xprt/svcxprt_rdma is pinned whenever it's possible that + * the Receive completion handler could be running. + */ +static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) +{ + struct svcxprt_rdma *rdma = cq->cq_context; + struct ib_cqe *cqe = wc->wr_cqe; + struct svc_rdma_recv_ctxt *ctxt; + + trace_svcrdma_wc_receive(wc); + + /* WARNING: Only wc->wr_cqe and wc->status are reliable */ + ctxt = container_of(cqe, struct svc_rdma_recv_ctxt, rc_cqe); + svc_rdma_recv_ctxt_unmap(rdma, ctxt); + + if (wc->status != IB_WC_SUCCESS) + goto flushed; + + if (svc_rdma_post_recv(rdma)) + goto post_err; + + /* All wc fields are now known to be valid */ + ctxt->rc_byte_len = wc->byte_len; + spin_lock(&rdma->sc_rq_dto_lock); + list_add_tail(&ctxt->rc_list, &rdma->sc_rq_dto_q); + spin_unlock(&rdma->sc_rq_dto_lock); + set_bit(XPT_DATA, &rdma->sc_xprt.xpt_flags); + if (!test_bit(RDMAXPRT_CONN_PENDING, &rdma->sc_flags)) + svc_xprt_enqueue(&rdma->sc_xprt); + goto out; + +flushed: + if (wc->status != IB_WC_WR_FLUSH_ERR) + pr_err("svcrdma: Recv: %s (%u/0x%x)\n", + ib_wc_status_msg(wc->status), + wc->status, wc->vendor_err); +post_err: + svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); + svc_xprt_enqueue(&rdma->sc_xprt); +out: + svc_xprt_put(&rdma->sc_xprt); +} + +/** + * svc_rdma_flush_recv_queues - Drain pending Receive work + * @rdma: svcxprt_rdma being shut down + * + */ +void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_recv_ctxt *ctxt; + + while ((ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_read_complete_q))) { + list_del(&ctxt->rc_list); + svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + } + while ((ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_rq_dto_q))) { + list_del(&ctxt->rc_list); + svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + } +} + /* * Replace the pages in the rq_argpages array with the pages from the SGE in * the RDMA_RECV completion. The SGL should contain full pages up until the * last one. */ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp, - struct svc_rdma_op_ctxt *ctxt) + struct svc_rdma_recv_ctxt *ctxt) { struct page *page; int sge_no; @@ -123,30 +357,30 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp, /* The reply path assumes the Call's transport header resides * in rqstp->rq_pages[0]. */ - page = ctxt->pages[0]; + page = ctxt->rc_pages[0]; put_page(rqstp->rq_pages[0]); rqstp->rq_pages[0] = page; /* Set up the XDR head */ rqstp->rq_arg.head[0].iov_base = page_address(page); rqstp->rq_arg.head[0].iov_len = - min_t(size_t, ctxt->byte_len, ctxt->sge[0].length); - rqstp->rq_arg.len = ctxt->byte_len; - rqstp->rq_arg.buflen = ctxt->byte_len; + min_t(size_t, ctxt->rc_byte_len, ctxt->rc_sges[0].length); + rqstp->rq_arg.len = ctxt->rc_byte_len; + rqstp->rq_arg.buflen = ctxt->rc_byte_len; /* Compute bytes past head in the SGL */ - len = ctxt->byte_len - rqstp->rq_arg.head[0].iov_len; + len = ctxt->rc_byte_len - rqstp->rq_arg.head[0].iov_len; /* If data remains, store it in the pagelist */ rqstp->rq_arg.page_len = len; rqstp->rq_arg.page_base = 0; sge_no = 1; - while (len && sge_no < ctxt->count) { - page = ctxt->pages[sge_no]; + while (len && sge_no < ctxt->rc_recv_wr.num_sge) { + page = ctxt->rc_pages[sge_no]; put_page(rqstp->rq_pages[sge_no]); rqstp->rq_pages[sge_no] = page; - len -= min_t(u32, len, ctxt->sge[sge_no].length); + len -= min_t(u32, len, ctxt->rc_sges[sge_no].length); sge_no++; } rqstp->rq_respages = &rqstp->rq_pages[sge_no]; @@ -154,11 +388,11 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp, /* If not all pages were used from the SGL, free the remaining ones */ len = sge_no; - while (sge_no < ctxt->count) { - page = ctxt->pages[sge_no++]; + while (sge_no < ctxt->rc_recv_wr.num_sge) { + page = ctxt->rc_pages[sge_no++]; put_page(page); } - ctxt->count = len; + ctxt->rc_page_count = len; /* Set up tail */ rqstp->rq_arg.tail[0].iov_base = NULL; @@ -364,29 +598,29 @@ out_inval: } static void rdma_read_complete(struct svc_rqst *rqstp, - struct svc_rdma_op_ctxt *head) + struct svc_rdma_recv_ctxt *head) { int page_no; /* Copy RPC pages */ - for (page_no = 0; page_no < head->count; page_no++) { + for (page_no = 0; page_no < head->rc_page_count; page_no++) { put_page(rqstp->rq_pages[page_no]); - rqstp->rq_pages[page_no] = head->pages[page_no]; + rqstp->rq_pages[page_no] = head->rc_pages[page_no]; } /* Point rq_arg.pages past header */ - rqstp->rq_arg.pages = &rqstp->rq_pages[head->hdr_count]; - rqstp->rq_arg.page_len = head->arg.page_len; + rqstp->rq_arg.pages = &rqstp->rq_pages[head->rc_hdr_count]; + rqstp->rq_arg.page_len = head->rc_arg.page_len; /* rq_respages starts after the last arg page */ rqstp->rq_respages = &rqstp->rq_pages[page_no]; rqstp->rq_next_page = rqstp->rq_respages + 1; /* Rebuild rq_arg head and tail. */ - rqstp->rq_arg.head[0] = head->arg.head[0]; - rqstp->rq_arg.tail[0] = head->arg.tail[0]; - rqstp->rq_arg.len = head->arg.len; - rqstp->rq_arg.buflen = head->arg.buflen; + rqstp->rq_arg.head[0] = head->rc_arg.head[0]; + rqstp->rq_arg.tail[0] = head->rc_arg.tail[0]; + rqstp->rq_arg.len = head->rc_arg.len; + rqstp->rq_arg.buflen = head->rc_arg.buflen; } static void svc_rdma_send_error(struct svcxprt_rdma *xprt, @@ -506,28 +740,26 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) struct svc_xprt *xprt = rqstp->rq_xprt; struct svcxprt_rdma *rdma_xprt = container_of(xprt, struct svcxprt_rdma, sc_xprt); - struct svc_rdma_op_ctxt *ctxt; + struct svc_rdma_recv_ctxt *ctxt; __be32 *p; int ret; spin_lock(&rdma_xprt->sc_rq_dto_lock); - if (!list_empty(&rdma_xprt->sc_read_complete_q)) { - ctxt = list_first_entry(&rdma_xprt->sc_read_complete_q, - struct svc_rdma_op_ctxt, list); - list_del(&ctxt->list); + ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_read_complete_q); + if (ctxt) { + list_del(&ctxt->rc_list); spin_unlock(&rdma_xprt->sc_rq_dto_lock); rdma_read_complete(rqstp, ctxt); goto complete; - } else if (!list_empty(&rdma_xprt->sc_rq_dto_q)) { - ctxt = list_first_entry(&rdma_xprt->sc_rq_dto_q, - struct svc_rdma_op_ctxt, list); - list_del(&ctxt->list); - } else { + } + ctxt = svc_rdma_next_recv_ctxt(&rdma_xprt->sc_rq_dto_q); + if (!ctxt) { /* No new incoming requests, terminate the loop */ clear_bit(XPT_DATA, &xprt->xpt_flags); spin_unlock(&rdma_xprt->sc_rq_dto_lock); return 0; } + list_del(&ctxt->rc_list); spin_unlock(&rdma_xprt->sc_rq_dto_lock); atomic_inc(&rdma_stat_recv); @@ -545,7 +777,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) if (svc_rdma_is_backchannel_reply(xprt, p)) { ret = svc_rdma_handle_bc_reply(xprt->xpt_bc_xprt, p, &rqstp->rq_arg); - svc_rdma_put_context(ctxt, 0); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 0); return ret; } @@ -554,7 +786,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) goto out_readchunk; complete: - svc_rdma_put_context(ctxt, 0); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 0); rqstp->rq_prot = IPPROTO_MAX; svc_xprt_copy_addrs(rqstp, xprt); return rqstp->rq_arg.len; @@ -567,16 +799,16 @@ out_readchunk: out_err: svc_rdma_send_error(rdma_xprt, p, ret); - svc_rdma_put_context(ctxt, 0); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 0); return 0; out_postfail: if (ret == -EINVAL) svc_rdma_send_error(rdma_xprt, p, ret); - svc_rdma_put_context(ctxt, 1); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 1); return ret; out_drop: - svc_rdma_put_context(ctxt, 1); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 1); return 0; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 887ceef125b2..c080ce20ff40 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2016 Oracle. All rights reserved. + * Copyright (c) 2016-2018 Oracle. All rights reserved. * * Use the core R/W API to move RPC-over-RDMA Read and Write chunks. */ @@ -227,7 +227,7 @@ static void svc_rdma_write_done(struct ib_cq *cq, struct ib_wc *wc) /* State for pulling a Read chunk. */ struct svc_rdma_read_info { - struct svc_rdma_op_ctxt *ri_readctxt; + struct svc_rdma_recv_ctxt *ri_readctxt; unsigned int ri_position; unsigned int ri_pageno; unsigned int ri_pageoff; @@ -282,10 +282,10 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) pr_err("svcrdma: read ctx: %s (%u/0x%x)\n", ib_wc_status_msg(wc->status), wc->status, wc->vendor_err); - svc_rdma_put_context(info->ri_readctxt, 1); + svc_rdma_recv_ctxt_put(rdma, info->ri_readctxt, 1); } else { spin_lock(&rdma->sc_rq_dto_lock); - list_add_tail(&info->ri_readctxt->list, + list_add_tail(&info->ri_readctxt->rc_list, &rdma->sc_read_complete_q); spin_unlock(&rdma->sc_rq_dto_lock); @@ -607,7 +607,7 @@ static int svc_rdma_build_read_segment(struct svc_rdma_read_info *info, struct svc_rqst *rqstp, u32 rkey, u32 len, u64 offset) { - struct svc_rdma_op_ctxt *head = info->ri_readctxt; + struct svc_rdma_recv_ctxt *head = info->ri_readctxt; struct svc_rdma_chunk_ctxt *cc = &info->ri_cc; struct svc_rdma_rw_ctxt *ctxt; unsigned int sge_no, seg_len; @@ -625,10 +625,10 @@ static int svc_rdma_build_read_segment(struct svc_rdma_read_info *info, seg_len = min_t(unsigned int, len, PAGE_SIZE - info->ri_pageoff); - head->arg.pages[info->ri_pageno] = + head->rc_arg.pages[info->ri_pageno] = rqstp->rq_pages[info->ri_pageno]; if (!info->ri_pageoff) - head->count++; + head->rc_page_count++; sg_set_page(sg, rqstp->rq_pages[info->ri_pageno], seg_len, info->ri_pageoff); @@ -705,9 +705,9 @@ static int svc_rdma_build_read_chunk(struct svc_rqst *rqstp, } /* Construct RDMA Reads to pull over a normal Read chunk. The chunk - * data lands in the page list of head->arg.pages. + * data lands in the page list of head->rc_arg.pages. * - * Currently NFSD does not look at the head->arg.tail[0] iovec. + * Currently NFSD does not look at the head->rc_arg.tail[0] iovec. * Therefore, XDR round-up of the Read chunk and trailing * inline content must both be added at the end of the pagelist. */ @@ -715,10 +715,10 @@ static int svc_rdma_build_normal_read_chunk(struct svc_rqst *rqstp, struct svc_rdma_read_info *info, __be32 *p) { - struct svc_rdma_op_ctxt *head = info->ri_readctxt; + struct svc_rdma_recv_ctxt *head = info->ri_readctxt; int ret; - info->ri_pageno = head->hdr_count; + info->ri_pageno = head->rc_hdr_count; info->ri_pageoff = 0; ret = svc_rdma_build_read_chunk(rqstp, info, p); @@ -732,11 +732,11 @@ static int svc_rdma_build_normal_read_chunk(struct svc_rqst *rqstp, * chunk is not included in either the pagelist or in * the tail. */ - head->arg.tail[0].iov_base = - head->arg.head[0].iov_base + info->ri_position; - head->arg.tail[0].iov_len = - head->arg.head[0].iov_len - info->ri_position; - head->arg.head[0].iov_len = info->ri_position; + head->rc_arg.tail[0].iov_base = + head->rc_arg.head[0].iov_base + info->ri_position; + head->rc_arg.tail[0].iov_len = + head->rc_arg.head[0].iov_len - info->ri_position; + head->rc_arg.head[0].iov_len = info->ri_position; /* Read chunk may need XDR roundup (see RFC 8166, s. 3.4.5.2). * @@ -749,9 +749,9 @@ static int svc_rdma_build_normal_read_chunk(struct svc_rqst *rqstp, */ info->ri_chunklen = XDR_QUADLEN(info->ri_chunklen) << 2; - head->arg.page_len = info->ri_chunklen; - head->arg.len += info->ri_chunklen; - head->arg.buflen += info->ri_chunklen; + head->rc_arg.page_len = info->ri_chunklen; + head->rc_arg.len += info->ri_chunklen; + head->rc_arg.buflen += info->ri_chunklen; out: return ret; @@ -760,7 +760,7 @@ out: /* Construct RDMA Reads to pull over a Position Zero Read chunk. * The start of the data lands in the first page just after * the Transport header, and the rest lands in the page list of - * head->arg.pages. + * head->rc_arg.pages. * * Assumptions: * - A PZRC has an XDR-aligned length (no implicit round-up). @@ -772,11 +772,11 @@ static int svc_rdma_build_pz_read_chunk(struct svc_rqst *rqstp, struct svc_rdma_read_info *info, __be32 *p) { - struct svc_rdma_op_ctxt *head = info->ri_readctxt; + struct svc_rdma_recv_ctxt *head = info->ri_readctxt; int ret; - info->ri_pageno = head->hdr_count - 1; - info->ri_pageoff = offset_in_page(head->byte_len); + info->ri_pageno = head->rc_hdr_count - 1; + info->ri_pageoff = offset_in_page(head->rc_byte_len); ret = svc_rdma_build_read_chunk(rqstp, info, p); if (ret < 0) @@ -784,22 +784,22 @@ static int svc_rdma_build_pz_read_chunk(struct svc_rqst *rqstp, trace_svcrdma_encode_pzr(info->ri_chunklen); - head->arg.len += info->ri_chunklen; - head->arg.buflen += info->ri_chunklen; + head->rc_arg.len += info->ri_chunklen; + head->rc_arg.buflen += info->ri_chunklen; - if (head->arg.buflen <= head->sge[0].length) { + if (head->rc_arg.buflen <= head->rc_sges[0].length) { /* Transport header and RPC message fit entirely * in page where head iovec resides. */ - head->arg.head[0].iov_len = info->ri_chunklen; + head->rc_arg.head[0].iov_len = info->ri_chunklen; } else { /* Transport header and part of RPC message reside * in the head iovec's page. */ - head->arg.head[0].iov_len = - head->sge[0].length - head->byte_len; - head->arg.page_len = - info->ri_chunklen - head->arg.head[0].iov_len; + head->rc_arg.head[0].iov_len = + head->rc_sges[0].length - head->rc_byte_len; + head->rc_arg.page_len = + info->ri_chunklen - head->rc_arg.head[0].iov_len; } out: @@ -824,24 +824,24 @@ out: * - All Read segments in @p have the same Position value. */ int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, - struct svc_rdma_op_ctxt *head, __be32 *p) + struct svc_rdma_recv_ctxt *head, __be32 *p) { struct svc_rdma_read_info *info; struct page **page; int ret; /* The request (with page list) is constructed in - * head->arg. Pages involved with RDMA Read I/O are + * head->rc_arg. Pages involved with RDMA Read I/O are * transferred there. */ - head->hdr_count = head->count; - head->arg.head[0] = rqstp->rq_arg.head[0]; - head->arg.tail[0] = rqstp->rq_arg.tail[0]; - head->arg.pages = head->pages; - head->arg.page_base = 0; - head->arg.page_len = 0; - head->arg.len = rqstp->rq_arg.len; - head->arg.buflen = rqstp->rq_arg.buflen; + head->rc_hdr_count = head->rc_page_count; + head->rc_arg.head[0] = rqstp->rq_arg.head[0]; + head->rc_arg.tail[0] = rqstp->rq_arg.tail[0]; + head->rc_arg.pages = head->rc_pages; + head->rc_arg.page_base = 0; + head->rc_arg.page_len = 0; + head->rc_arg.len = rqstp->rq_arg.len; + head->rc_arg.buflen = rqstp->rq_arg.buflen; info = svc_rdma_read_info_alloc(rdma); if (!info) @@ -867,7 +867,7 @@ int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, out: /* Read sink pages have been moved from rqstp->rq_pages to - * head->arg.pages. Force svc_recv to refill those slots + * head->rc_arg.pages. Force svc_recv to refill those slots * in rq_pages. */ for (page = rqstp->rq_pages; page < rqstp->rq_respages; page++) diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index fed28de78d37..a397d9a3d80e 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (c) 2016 Oracle. All rights reserved. + * Copyright (c) 2016-2018 Oracle. All rights reserved. * Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved. * Copyright (c) 2005-2006 Network Appliance, Inc. All rights reserved. * diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 05edb18f8ca3..05544f2f50d4 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -63,7 +63,6 @@ #define RPCDBG_FACILITY RPCDBG_SVCXPRT -static int svc_rdma_post_recv(struct svcxprt_rdma *xprt); static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, struct net *net); static struct svc_xprt *svc_rdma_create(struct svc_serv *serv, @@ -175,11 +174,7 @@ static bool svc_rdma_prealloc_ctxts(struct svcxprt_rdma *xprt) { unsigned int i; - /* Each RPC/RDMA credit can consume one Receive and - * one Send WQE at the same time. - */ - i = xprt->sc_sq_depth + xprt->sc_rq_depth; - + i = xprt->sc_sq_depth; while (i--) { struct svc_rdma_op_ctxt *ctxt; @@ -297,54 +292,6 @@ static void qp_event_handler(struct ib_event *event, void *context) } } -/** - * svc_rdma_wc_receive - Invoked by RDMA provider for each polled Receive WC - * @cq: completion queue - * @wc: completed WR - * - */ -static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) -{ - struct svcxprt_rdma *xprt = cq->cq_context; - struct ib_cqe *cqe = wc->wr_cqe; - struct svc_rdma_op_ctxt *ctxt; - - trace_svcrdma_wc_receive(wc); - - /* WARNING: Only wc->wr_cqe and wc->status are reliable */ - ctxt = container_of(cqe, struct svc_rdma_op_ctxt, cqe); - svc_rdma_unmap_dma(ctxt); - - if (wc->status != IB_WC_SUCCESS) - goto flushed; - - /* All wc fields are now known to be valid */ - ctxt->byte_len = wc->byte_len; - spin_lock(&xprt->sc_rq_dto_lock); - list_add_tail(&ctxt->list, &xprt->sc_rq_dto_q); - spin_unlock(&xprt->sc_rq_dto_lock); - - svc_rdma_post_recv(xprt); - - set_bit(XPT_DATA, &xprt->sc_xprt.xpt_flags); - if (test_bit(RDMAXPRT_CONN_PENDING, &xprt->sc_flags)) - goto out; - goto out_enqueue; - -flushed: - if (wc->status != IB_WC_WR_FLUSH_ERR) - pr_err("svcrdma: Recv: %s (%u/0x%x)\n", - ib_wc_status_msg(wc->status), - wc->status, wc->vendor_err); - set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags); - svc_rdma_put_context(ctxt, 1); - -out_enqueue: - svc_xprt_enqueue(&xprt->sc_xprt); -out: - svc_xprt_put(&xprt->sc_xprt); -} - /** * svc_rdma_wc_send - Invoked by RDMA provider for each polled Send WC * @cq: completion queue @@ -392,12 +339,14 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_read_complete_q); INIT_LIST_HEAD(&cma_xprt->sc_ctxts); + INIT_LIST_HEAD(&cma_xprt->sc_recv_ctxts); INIT_LIST_HEAD(&cma_xprt->sc_rw_ctxts); init_waitqueue_head(&cma_xprt->sc_send_wait); spin_lock_init(&cma_xprt->sc_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); spin_lock_init(&cma_xprt->sc_ctxt_lock); + spin_lock_init(&cma_xprt->sc_recv_lock); spin_lock_init(&cma_xprt->sc_rw_ctxt_lock); /* @@ -411,63 +360,6 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, return cma_xprt; } -static int -svc_rdma_post_recv(struct svcxprt_rdma *xprt) -{ - struct ib_recv_wr recv_wr, *bad_recv_wr; - struct svc_rdma_op_ctxt *ctxt; - struct page *page; - dma_addr_t pa; - int sge_no; - int buflen; - int ret; - - ctxt = svc_rdma_get_context(xprt); - buflen = 0; - ctxt->direction = DMA_FROM_DEVICE; - ctxt->cqe.done = svc_rdma_wc_receive; - for (sge_no = 0; buflen < xprt->sc_max_req_size; sge_no++) { - if (sge_no >= xprt->sc_max_sge) { - pr_err("svcrdma: Too many sges (%d)\n", sge_no); - goto err_put_ctxt; - } - page = alloc_page(GFP_KERNEL); - if (!page) - goto err_put_ctxt; - ctxt->pages[sge_no] = page; - pa = ib_dma_map_page(xprt->sc_cm_id->device, - page, 0, PAGE_SIZE, - DMA_FROM_DEVICE); - if (ib_dma_mapping_error(xprt->sc_cm_id->device, pa)) - goto err_put_ctxt; - svc_rdma_count_mappings(xprt, ctxt); - ctxt->sge[sge_no].addr = pa; - ctxt->sge[sge_no].length = PAGE_SIZE; - ctxt->sge[sge_no].lkey = xprt->sc_pd->local_dma_lkey; - ctxt->count = sge_no + 1; - buflen += PAGE_SIZE; - } - recv_wr.next = NULL; - recv_wr.sg_list = &ctxt->sge[0]; - recv_wr.num_sge = ctxt->count; - recv_wr.wr_cqe = &ctxt->cqe; - - svc_xprt_get(&xprt->sc_xprt); - ret = ib_post_recv(xprt->sc_qp, &recv_wr, &bad_recv_wr); - trace_svcrdma_post_recv(&recv_wr, ret); - if (ret) { - svc_rdma_unmap_dma(ctxt); - svc_rdma_put_context(ctxt, 1); - svc_xprt_put(&xprt->sc_xprt); - } - return ret; - - err_put_ctxt: - svc_rdma_unmap_dma(ctxt); - svc_rdma_put_context(ctxt, 1); - return -ENOMEM; -} - static void svc_rdma_parse_connect_private(struct svcxprt_rdma *newxprt, struct rdma_conn_param *param) @@ -698,7 +590,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) struct ib_qp_init_attr qp_attr; struct ib_device *dev; struct sockaddr *sap; - unsigned int i, ctxts; + unsigned int ctxts; int ret = 0; listen_rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); @@ -803,14 +695,8 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) !rdma_ib_or_roce(dev, newxprt->sc_port_num)) goto errout; - /* Post receive buffers */ - for (i = 0; i < newxprt->sc_max_requests; i++) { - ret = svc_rdma_post_recv(newxprt); - if (ret) { - dprintk("svcrdma: failure posting receive buffers\n"); - goto errout; - } - } + if (!svc_rdma_post_recvs(newxprt)) + goto errout; /* Swap out the handler */ newxprt->sc_cm_id->event_handler = rdma_cma_handler; @@ -907,20 +793,7 @@ static void __svc_rdma_free(struct work_struct *work) pr_err("svcrdma: sc_xprt still in use? (%d)\n", kref_read(&xprt->xpt_ref)); - while (!list_empty(&rdma->sc_read_complete_q)) { - struct svc_rdma_op_ctxt *ctxt; - ctxt = list_first_entry(&rdma->sc_read_complete_q, - struct svc_rdma_op_ctxt, list); - list_del(&ctxt->list); - svc_rdma_put_context(ctxt, 1); - } - while (!list_empty(&rdma->sc_rq_dto_q)) { - struct svc_rdma_op_ctxt *ctxt; - ctxt = list_first_entry(&rdma->sc_rq_dto_q, - struct svc_rdma_op_ctxt, list); - list_del(&ctxt->list); - svc_rdma_put_context(ctxt, 1); - } + svc_rdma_flush_recv_queues(rdma); /* Warn if we leaked a resource or under-referenced */ if (rdma->sc_ctxt_used != 0) @@ -935,6 +808,7 @@ static void __svc_rdma_free(struct work_struct *work) svc_rdma_destroy_rw_ctxts(rdma); svc_rdma_destroy_ctxts(rdma); + svc_rdma_recv_ctxts_destroy(rdma); /* Destroy the QP if present (not a listener) */ if (rdma->sc_qp && !IS_ERR(rdma->sc_qp)) -- cgit From 2c577bfea85e421bfa91df16ccf5156361aa8d4b Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:27:27 -0400 Subject: svcrdma: Remove sc_rq_depth Clean up: No need to retain rq_depth in struct svcrdma_xprt, it is used only in svc_rdma_accept(). Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 1 - net/sunrpc/xprtrdma/svc_rdma_transport.c | 17 ++++++++--------- 2 files changed, 8 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 37f759d65348..3cb66319a814 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -101,7 +101,6 @@ struct svcxprt_rdma { atomic_t sc_sq_avail; /* SQEs ready to be consumed */ unsigned int sc_sq_depth; /* Depth of SQ */ - unsigned int sc_rq_depth; /* Depth of RQ */ __be32 sc_fc_credits; /* Forward credits */ u32 sc_max_requests; /* Max requests */ u32 sc_max_bc_requests;/* Backward credits */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 05544f2f50d4..ef32c46a234c 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -588,9 +588,9 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) struct rdma_conn_param conn_param; struct rpcrdma_connect_private pmsg; struct ib_qp_init_attr qp_attr; + unsigned int ctxts, rq_depth; struct ib_device *dev; struct sockaddr *sap; - unsigned int ctxts; int ret = 0; listen_rdma = container_of(xprt, struct svcxprt_rdma, sc_xprt); @@ -621,19 +621,18 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) newxprt->sc_max_req_size = svcrdma_max_req_size; newxprt->sc_max_requests = svcrdma_max_requests; newxprt->sc_max_bc_requests = svcrdma_max_bc_requests; - newxprt->sc_rq_depth = newxprt->sc_max_requests + - newxprt->sc_max_bc_requests; - if (newxprt->sc_rq_depth > dev->attrs.max_qp_wr) { + rq_depth = newxprt->sc_max_requests + newxprt->sc_max_bc_requests; + if (rq_depth > dev->attrs.max_qp_wr) { pr_warn("svcrdma: reducing receive depth to %d\n", dev->attrs.max_qp_wr); - newxprt->sc_rq_depth = dev->attrs.max_qp_wr; - newxprt->sc_max_requests = newxprt->sc_rq_depth - 2; + rq_depth = dev->attrs.max_qp_wr; + newxprt->sc_max_requests = rq_depth - 2; newxprt->sc_max_bc_requests = 2; } newxprt->sc_fc_credits = cpu_to_be32(newxprt->sc_max_requests); ctxts = rdma_rw_mr_factor(dev, newxprt->sc_port_num, RPCSVC_MAXPAGES); ctxts *= newxprt->sc_max_requests; - newxprt->sc_sq_depth = newxprt->sc_rq_depth + ctxts; + newxprt->sc_sq_depth = rq_depth + ctxts; if (newxprt->sc_sq_depth > dev->attrs.max_qp_wr) { pr_warn("svcrdma: reducing send depth to %d\n", dev->attrs.max_qp_wr); @@ -655,7 +654,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) dprintk("svcrdma: error creating SQ CQ for connect request\n"); goto errout; } - newxprt->sc_rq_cq = ib_alloc_cq(dev, newxprt, newxprt->sc_rq_depth, + newxprt->sc_rq_cq = ib_alloc_cq(dev, newxprt, rq_depth, 0, IB_POLL_WORKQUEUE); if (IS_ERR(newxprt->sc_rq_cq)) { dprintk("svcrdma: error creating RQ CQ for connect request\n"); @@ -668,7 +667,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) qp_attr.port_num = newxprt->sc_port_num; qp_attr.cap.max_rdma_ctxs = ctxts; qp_attr.cap.max_send_wr = newxprt->sc_sq_depth - ctxts; - qp_attr.cap.max_recv_wr = newxprt->sc_rq_depth; + qp_attr.cap.max_recv_wr = rq_depth; qp_attr.cap.max_send_sge = newxprt->sc_max_sge; qp_attr.cap.max_recv_sge = newxprt->sc_max_sge; qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; -- cgit From 1e5f4160745690a0476929d128a336cae95c1df9 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:27:32 -0400 Subject: svcrdma: Simplify svc_rdma_recv_ctxt_put Currently svc_rdma_recv_ctxt_put's callers have to know whether they want to free the ctxt's pages or not. This means the human developers have to know when and why to set that free_pages argument. Instead, the ctxt should carry that information with it so that svc_rdma_recv_ctxt_put does the right thing no matter who is calling. We want to keep track of the number of pages in the Receive buffer separately from the number of pages pulled over by RDMA Read. This is so that the correct number of pages can be freed properly and that number is well-documented. So now, rc_hdr_count is the number of pages consumed by head[0] (ie., the page index where the Read chunk should start); and rc_page_count is always the number of pages that need to be released when the ctxt is put. The @free_pages argument is no longer needed. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 3 +-- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 41 ++++++++++++++++++--------------- net/sunrpc/xprtrdma/svc_rdma_rw.c | 4 ++-- 3 files changed, 25 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 3cb66319a814..f0bd0b6d8931 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -173,8 +173,7 @@ extern int svc_rdma_handle_bc_reply(struct rpc_xprt *xprt, extern void svc_rdma_recv_ctxts_destroy(struct svcxprt_rdma *rdma); extern bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma); extern void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, - struct svc_rdma_recv_ctxt *ctxt, - int free_pages); + struct svc_rdma_recv_ctxt *ctxt); extern void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma); extern int svc_rdma_recvfrom(struct svc_rqst *); diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index b7d9c55ee896..ecfe7c90a268 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -175,18 +175,15 @@ static void svc_rdma_recv_ctxt_unmap(struct svcxprt_rdma *rdma, * svc_rdma_recv_ctxt_put - Return recv_ctxt to free list * @rdma: controlling svcxprt_rdma * @ctxt: object to return to the free list - * @free_pages: Non-zero if rc_pages should be freed * */ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, - struct svc_rdma_recv_ctxt *ctxt, - int free_pages) + struct svc_rdma_recv_ctxt *ctxt) { unsigned int i; - if (free_pages) - for (i = 0; i < ctxt->rc_page_count; i++) - put_page(ctxt->rc_pages[i]); + for (i = 0; i < ctxt->rc_page_count; i++) + put_page(ctxt->rc_pages[i]); spin_lock(&rdma->sc_recv_lock); list_add(&ctxt->rc_list, &rdma->sc_recv_ctxts); spin_unlock(&rdma->sc_recv_lock); @@ -243,11 +240,11 @@ static int svc_rdma_post_recv(struct svcxprt_rdma *rdma) err_put_ctxt: svc_rdma_recv_ctxt_unmap(rdma, ctxt); - svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + svc_rdma_recv_ctxt_put(rdma, ctxt); return -ENOMEM; err_post: svc_rdma_recv_ctxt_unmap(rdma, ctxt); - svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + svc_rdma_recv_ctxt_put(rdma, ctxt); svc_xprt_put(&rdma->sc_xprt); return ret; } @@ -316,7 +313,7 @@ flushed: ib_wc_status_msg(wc->status), wc->status, wc->vendor_err); post_err: - svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + svc_rdma_recv_ctxt_put(rdma, ctxt); set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); svc_xprt_enqueue(&rdma->sc_xprt); out: @@ -334,11 +331,11 @@ void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma) while ((ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_read_complete_q))) { list_del(&ctxt->rc_list); - svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + svc_rdma_recv_ctxt_put(rdma, ctxt); } while ((ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_rq_dto_q))) { list_del(&ctxt->rc_list); - svc_rdma_recv_ctxt_put(rdma, ctxt, 1); + svc_rdma_recv_ctxt_put(rdma, ctxt); } } @@ -383,16 +380,19 @@ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp, len -= min_t(u32, len, ctxt->rc_sges[sge_no].length); sge_no++; } + ctxt->rc_hdr_count = sge_no; rqstp->rq_respages = &rqstp->rq_pages[sge_no]; rqstp->rq_next_page = rqstp->rq_respages + 1; /* If not all pages were used from the SGL, free the remaining ones */ - len = sge_no; while (sge_no < ctxt->rc_recv_wr.num_sge) { page = ctxt->rc_pages[sge_no++]; put_page(page); } - ctxt->rc_page_count = len; + + /* @ctxt's pages have all been released or moved to @rqstp->rq_pages. + */ + ctxt->rc_page_count = 0; /* Set up tail */ rqstp->rq_arg.tail[0].iov_base = NULL; @@ -602,11 +602,14 @@ static void rdma_read_complete(struct svc_rqst *rqstp, { int page_no; - /* Copy RPC pages */ + /* Move Read chunk pages to rqstp so that they will be released + * when svc_process is done with them. + */ for (page_no = 0; page_no < head->rc_page_count; page_no++) { put_page(rqstp->rq_pages[page_no]); rqstp->rq_pages[page_no] = head->rc_pages[page_no]; } + head->rc_page_count = 0; /* Point rq_arg.pages past header */ rqstp->rq_arg.pages = &rqstp->rq_pages[head->rc_hdr_count]; @@ -777,7 +780,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) if (svc_rdma_is_backchannel_reply(xprt, p)) { ret = svc_rdma_handle_bc_reply(xprt->xpt_bc_xprt, p, &rqstp->rq_arg); - svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 0); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); return ret; } @@ -786,7 +789,7 @@ int svc_rdma_recvfrom(struct svc_rqst *rqstp) goto out_readchunk; complete: - svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 0); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); rqstp->rq_prot = IPPROTO_MAX; svc_xprt_copy_addrs(rqstp, xprt); return rqstp->rq_arg.len; @@ -799,16 +802,16 @@ out_readchunk: out_err: svc_rdma_send_error(rdma_xprt, p, ret); - svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 0); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); return 0; out_postfail: if (ret == -EINVAL) svc_rdma_send_error(rdma_xprt, p, ret); - svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 1); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); return ret; out_drop: - svc_rdma_recv_ctxt_put(rdma_xprt, ctxt, 1); + svc_rdma_recv_ctxt_put(rdma_xprt, ctxt); return 0; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index c080ce20ff40..8242aa318ac1 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -282,7 +282,7 @@ static void svc_rdma_wc_read_done(struct ib_cq *cq, struct ib_wc *wc) pr_err("svcrdma: read ctx: %s (%u/0x%x)\n", ib_wc_status_msg(wc->status), wc->status, wc->vendor_err); - svc_rdma_recv_ctxt_put(rdma, info->ri_readctxt, 1); + svc_rdma_recv_ctxt_put(rdma, info->ri_readctxt); } else { spin_lock(&rdma->sc_rq_dto_lock); list_add_tail(&info->ri_readctxt->rc_list, @@ -834,7 +834,7 @@ int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, * head->rc_arg. Pages involved with RDMA Read I/O are * transferred there. */ - head->rc_hdr_count = head->rc_page_count; + head->rc_page_count = head->rc_hdr_count; head->rc_arg.head[0] = rqstp->rq_arg.head[0]; head->rc_arg.tail[0] = rqstp->rq_arg.tail[0]; head->rc_arg.pages = head->rc_pages; -- cgit From 3316f0631139c87631f2652c118da1a0354bd40d Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:27:43 -0400 Subject: svcrdma: Persistently allocate and DMA-map Receive buffers The current Receive path uses an array of pages which are allocated and DMA mapped when each Receive WR is posted, and then handed off to the upper layer in rqstp::rq_arg. The page flip releases unused pages in the rq_pages pagelist. This mechanism introduces a significant amount of overhead. So instead, kmalloc the Receive buffer, and leave it DMA-mapped while the transport remains connected. This confers a number of benefits: * Each Receive WR requires only one receive SGE, no matter how large the inline threshold is. This helps the server-side NFS/RDMA transport operate on less capable RDMA devices. * The Receive buffer is left allocated and mapped all the time. This relieves svc_rdma_post_recv from the overhead of allocating and DMA-mapping a fresh buffer. * svc_rdma_wc_receive no longer has to DMA unmap the Receive buffer. It has to DMA sync only the number of bytes that were received. * svc_rdma_build_arg_xdr no longer has to free a page in rq_pages for each page in the Receive buffer, making it a constant-time function. * The Receive buffer is now plugged directly into the rq_arg's head[0].iov_vec, and can be larger than a page without spilling over into rq_arg's page list. This enables simplification of the RDMA Read path in subsequent patches. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 4 +- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 168 +++++++++++-------------------- net/sunrpc/xprtrdma/svc_rdma_rw.c | 32 ++---- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 5 +- net/sunrpc/xprtrdma/svc_rdma_transport.c | 2 +- 5 files changed, 75 insertions(+), 136 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index f0bd0b6d8931..01baabfb863b 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -148,12 +148,12 @@ struct svc_rdma_recv_ctxt { struct list_head rc_list; struct ib_recv_wr rc_recv_wr; struct ib_cqe rc_cqe; + struct ib_sge rc_recv_sge; + void *rc_recv_buf; struct xdr_buf rc_arg; u32 rc_byte_len; unsigned int rc_page_count; unsigned int rc_hdr_count; - struct ib_sge rc_sges[1 + - RPCRDMA_MAX_INLINE_THRESH / PAGE_SIZE]; struct page *rc_pages[RPCSVC_MAXPAGES]; }; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index d9fef5211116..d4ccd1c0142c 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -117,6 +117,43 @@ svc_rdma_next_recv_ctxt(struct list_head *list) rc_list); } +static struct svc_rdma_recv_ctxt * +svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_recv_ctxt *ctxt; + dma_addr_t addr; + void *buffer; + + ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + goto fail0; + buffer = kmalloc(rdma->sc_max_req_size, GFP_KERNEL); + if (!buffer) + goto fail1; + addr = ib_dma_map_single(rdma->sc_pd->device, buffer, + rdma->sc_max_req_size, DMA_FROM_DEVICE); + if (ib_dma_mapping_error(rdma->sc_pd->device, addr)) + goto fail2; + + ctxt->rc_recv_wr.next = NULL; + ctxt->rc_recv_wr.wr_cqe = &ctxt->rc_cqe; + ctxt->rc_recv_wr.sg_list = &ctxt->rc_recv_sge; + ctxt->rc_recv_wr.num_sge = 1; + ctxt->rc_cqe.done = svc_rdma_wc_receive; + ctxt->rc_recv_sge.addr = addr; + ctxt->rc_recv_sge.length = rdma->sc_max_req_size; + ctxt->rc_recv_sge.lkey = rdma->sc_pd->local_dma_lkey; + ctxt->rc_recv_buf = buffer; + return ctxt; + +fail2: + kfree(buffer); +fail1: + kfree(ctxt); +fail0: + return NULL; +} + /** * svc_rdma_recv_ctxts_destroy - Release all recv_ctxt's for an xprt * @rdma: svcxprt_rdma being torn down @@ -128,6 +165,11 @@ void svc_rdma_recv_ctxts_destroy(struct svcxprt_rdma *rdma) while ((ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_recv_ctxts))) { list_del(&ctxt->rc_list); + ib_dma_unmap_single(rdma->sc_pd->device, + ctxt->rc_recv_sge.addr, + ctxt->rc_recv_sge.length, + DMA_FROM_DEVICE); + kfree(ctxt->rc_recv_buf); kfree(ctxt); } } @@ -145,32 +187,18 @@ svc_rdma_recv_ctxt_get(struct svcxprt_rdma *rdma) spin_unlock(&rdma->sc_recv_lock); out: - ctxt->rc_recv_wr.num_sge = 0; ctxt->rc_page_count = 0; return ctxt; out_empty: spin_unlock(&rdma->sc_recv_lock); - ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); + ctxt = svc_rdma_recv_ctxt_alloc(rdma); if (!ctxt) return NULL; goto out; } -static void svc_rdma_recv_ctxt_unmap(struct svcxprt_rdma *rdma, - struct svc_rdma_recv_ctxt *ctxt) -{ - struct ib_device *device = rdma->sc_cm_id->device; - int i; - - for (i = 0; i < ctxt->rc_recv_wr.num_sge; i++) - ib_dma_unmap_page(device, - ctxt->rc_sges[i].addr, - ctxt->rc_sges[i].length, - DMA_FROM_DEVICE); -} - /** * svc_rdma_recv_ctxt_put - Return recv_ctxt to free list * @rdma: controlling svcxprt_rdma @@ -191,46 +219,14 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, static int svc_rdma_post_recv(struct svcxprt_rdma *rdma) { - struct ib_device *device = rdma->sc_cm_id->device; struct svc_rdma_recv_ctxt *ctxt; struct ib_recv_wr *bad_recv_wr; - int sge_no, buflen, ret; - struct page *page; - dma_addr_t pa; + int ret; ctxt = svc_rdma_recv_ctxt_get(rdma); if (!ctxt) return -ENOMEM; - buflen = 0; - ctxt->rc_cqe.done = svc_rdma_wc_receive; - for (sge_no = 0; buflen < rdma->sc_max_req_size; sge_no++) { - if (sge_no >= rdma->sc_max_sge) { - pr_err("svcrdma: Too many sges (%d)\n", sge_no); - goto err_put_ctxt; - } - - page = alloc_page(GFP_KERNEL); - if (!page) - goto err_put_ctxt; - ctxt->rc_pages[sge_no] = page; - ctxt->rc_page_count++; - - pa = ib_dma_map_page(device, ctxt->rc_pages[sge_no], - 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (ib_dma_mapping_error(device, pa)) - goto err_put_ctxt; - ctxt->rc_sges[sge_no].addr = pa; - ctxt->rc_sges[sge_no].length = PAGE_SIZE; - ctxt->rc_sges[sge_no].lkey = rdma->sc_pd->local_dma_lkey; - ctxt->rc_recv_wr.num_sge++; - - buflen += PAGE_SIZE; - } - ctxt->rc_recv_wr.next = NULL; - ctxt->rc_recv_wr.sg_list = &ctxt->rc_sges[0]; - ctxt->rc_recv_wr.wr_cqe = &ctxt->rc_cqe; - svc_xprt_get(&rdma->sc_xprt); ret = ib_post_recv(rdma->sc_qp, &ctxt->rc_recv_wr, &bad_recv_wr); trace_svcrdma_post_recv(&ctxt->rc_recv_wr, ret); @@ -238,12 +234,7 @@ static int svc_rdma_post_recv(struct svcxprt_rdma *rdma) goto err_post; return 0; -err_put_ctxt: - svc_rdma_recv_ctxt_unmap(rdma, ctxt); - svc_rdma_recv_ctxt_put(rdma, ctxt); - return -ENOMEM; err_post: - svc_rdma_recv_ctxt_unmap(rdma, ctxt); svc_rdma_recv_ctxt_put(rdma, ctxt); svc_xprt_put(&rdma->sc_xprt); return ret; @@ -289,7 +280,6 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) /* WARNING: Only wc->wr_cqe and wc->status are reliable */ ctxt = container_of(cqe, struct svc_rdma_recv_ctxt, rc_cqe); - svc_rdma_recv_ctxt_unmap(rdma, ctxt); if (wc->status != IB_WC_SUCCESS) goto flushed; @@ -299,6 +289,10 @@ static void svc_rdma_wc_receive(struct ib_cq *cq, struct ib_wc *wc) /* All wc fields are now known to be valid */ ctxt->rc_byte_len = wc->byte_len; + ib_dma_sync_single_for_cpu(rdma->sc_pd->device, + ctxt->rc_recv_sge.addr, + wc->byte_len, DMA_FROM_DEVICE); + spin_lock(&rdma->sc_rq_dto_lock); list_add_tail(&ctxt->rc_list, &rdma->sc_rq_dto_q); spin_unlock(&rdma->sc_rq_dto_lock); @@ -339,64 +333,22 @@ void svc_rdma_flush_recv_queues(struct svcxprt_rdma *rdma) } } -/* - * Replace the pages in the rq_argpages array with the pages from the SGE in - * the RDMA_RECV completion. The SGL should contain full pages up until the - * last one. - */ static void svc_rdma_build_arg_xdr(struct svc_rqst *rqstp, struct svc_rdma_recv_ctxt *ctxt) { - struct page *page; - int sge_no; - u32 len; - - /* The reply path assumes the Call's transport header resides - * in rqstp->rq_pages[0]. - */ - page = ctxt->rc_pages[0]; - put_page(rqstp->rq_pages[0]); - rqstp->rq_pages[0] = page; - - /* Set up the XDR head */ - rqstp->rq_arg.head[0].iov_base = page_address(page); - rqstp->rq_arg.head[0].iov_len = - min_t(size_t, ctxt->rc_byte_len, ctxt->rc_sges[0].length); - rqstp->rq_arg.len = ctxt->rc_byte_len; - rqstp->rq_arg.buflen = ctxt->rc_byte_len; - - /* Compute bytes past head in the SGL */ - len = ctxt->rc_byte_len - rqstp->rq_arg.head[0].iov_len; - - /* If data remains, store it in the pagelist */ - rqstp->rq_arg.page_len = len; - rqstp->rq_arg.page_base = 0; - - sge_no = 1; - while (len && sge_no < ctxt->rc_recv_wr.num_sge) { - page = ctxt->rc_pages[sge_no]; - put_page(rqstp->rq_pages[sge_no]); - rqstp->rq_pages[sge_no] = page; - len -= min_t(u32, len, ctxt->rc_sges[sge_no].length); - sge_no++; - } - ctxt->rc_hdr_count = sge_no; - rqstp->rq_respages = &rqstp->rq_pages[sge_no]; + struct xdr_buf *arg = &rqstp->rq_arg; + + arg->head[0].iov_base = ctxt->rc_recv_buf; + arg->head[0].iov_len = ctxt->rc_byte_len; + arg->tail[0].iov_base = NULL; + arg->tail[0].iov_len = 0; + arg->page_len = 0; + arg->page_base = 0; + arg->buflen = ctxt->rc_byte_len; + arg->len = ctxt->rc_byte_len; + + rqstp->rq_respages = &rqstp->rq_pages[0]; rqstp->rq_next_page = rqstp->rq_respages + 1; - - /* If not all pages were used from the SGL, free the remaining ones */ - while (sge_no < ctxt->rc_recv_wr.num_sge) { - page = ctxt->rc_pages[sge_no++]; - put_page(page); - } - - /* @ctxt's pages have all been released or moved to @rqstp->rq_pages. - */ - ctxt->rc_page_count = 0; - - /* Set up tail */ - rqstp->rq_arg.tail[0].iov_base = NULL; - rqstp->rq_arg.tail[0].iov_len = 0; } /* This accommodates the largest possible Write chunk, diff --git a/net/sunrpc/xprtrdma/svc_rdma_rw.c b/net/sunrpc/xprtrdma/svc_rdma_rw.c index 8242aa318ac1..ce3ea8419704 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_rw.c +++ b/net/sunrpc/xprtrdma/svc_rdma_rw.c @@ -718,15 +718,14 @@ static int svc_rdma_build_normal_read_chunk(struct svc_rqst *rqstp, struct svc_rdma_recv_ctxt *head = info->ri_readctxt; int ret; - info->ri_pageno = head->rc_hdr_count; - info->ri_pageoff = 0; - ret = svc_rdma_build_read_chunk(rqstp, info, p); if (ret < 0) goto out; trace_svcrdma_encode_read(info->ri_chunklen, info->ri_position); + head->rc_hdr_count = 0; + /* Split the Receive buffer between the head and tail * buffers at Read chunk's position. XDR roundup of the * chunk is not included in either the pagelist or in @@ -775,9 +774,6 @@ static int svc_rdma_build_pz_read_chunk(struct svc_rqst *rqstp, struct svc_rdma_recv_ctxt *head = info->ri_readctxt; int ret; - info->ri_pageno = head->rc_hdr_count - 1; - info->ri_pageoff = offset_in_page(head->rc_byte_len); - ret = svc_rdma_build_read_chunk(rqstp, info, p); if (ret < 0) goto out; @@ -787,20 +783,13 @@ static int svc_rdma_build_pz_read_chunk(struct svc_rqst *rqstp, head->rc_arg.len += info->ri_chunklen; head->rc_arg.buflen += info->ri_chunklen; - if (head->rc_arg.buflen <= head->rc_sges[0].length) { - /* Transport header and RPC message fit entirely - * in page where head iovec resides. - */ - head->rc_arg.head[0].iov_len = info->ri_chunklen; - } else { - /* Transport header and part of RPC message reside - * in the head iovec's page. - */ - head->rc_arg.head[0].iov_len = - head->rc_sges[0].length - head->rc_byte_len; - head->rc_arg.page_len = - info->ri_chunklen - head->rc_arg.head[0].iov_len; - } + head->rc_hdr_count = 1; + head->rc_arg.head[0].iov_base = page_address(head->rc_pages[0]); + head->rc_arg.head[0].iov_len = min_t(size_t, PAGE_SIZE, + info->ri_chunklen); + + head->rc_arg.page_len = info->ri_chunklen - + head->rc_arg.head[0].iov_len; out: return ret; @@ -834,7 +823,6 @@ int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, * head->rc_arg. Pages involved with RDMA Read I/O are * transferred there. */ - head->rc_page_count = head->rc_hdr_count; head->rc_arg.head[0] = rqstp->rq_arg.head[0]; head->rc_arg.tail[0] = rqstp->rq_arg.tail[0]; head->rc_arg.pages = head->rc_pages; @@ -847,6 +835,8 @@ int svc_rdma_recv_read_chunk(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, if (!info) return -ENOMEM; info->ri_readctxt = head; + info->ri_pageno = 0; + info->ri_pageoff = 0; info->ri_position = be32_to_cpup(p + 1); if (info->ri_position) diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index cbbde70eeec5..b27b597d94da 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -629,10 +629,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) struct page *res_page; int ret; - /* Find the call's chunk lists to decide how to send the reply. - * Receive places the Call's xprt header at the start of page 0. - */ - rdma_argp = page_address(rqstp->rq_pages[0]); + rdma_argp = rctxt->rc_recv_buf; svc_rdma_get_write_arrays(rdma_argp, &wr_lst, &rp_ch); /* Create the RDMA response header. xprt->xpt_mutex, diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index ef32c46a234c..baeecbb2f763 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -669,7 +669,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) qp_attr.cap.max_send_wr = newxprt->sc_sq_depth - ctxts; qp_attr.cap.max_recv_wr = rq_depth; qp_attr.cap.max_send_sge = newxprt->sc_max_sge; - qp_attr.cap.max_recv_sge = newxprt->sc_max_sge; + qp_attr.cap.max_recv_sge = 1; qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; qp_attr.qp_type = IB_QPT_RC; qp_attr.send_cq = newxprt->sc_sq_cq; -- cgit From eb5d7a622e0bbe3fd316b2325d3840a0e030a3c4 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:27:48 -0400 Subject: svcrdma: Allocate recv_ctxt's on CPU handling Receives There is a significant latency penalty when processing an ingress Receive if the Receive buffer resides in memory that is not on the same NUMA node as the the CPU handling completions for a CQ. The system administrator and the device driver determine which CPU handles completions. This CPU does not change during life of the CQ. Further the Upper Layer does not have any visibility of which CPU it is. Allocating Receive buffers in the Receive completion handler guarantees that Receive buffers are allocated on the preferred NUMA node for that CQ. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 1 + net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 52 +++++++++++++++++++++++---------- 2 files changed, 37 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 01baabfb863b..27cf59c7085f 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -151,6 +151,7 @@ struct svc_rdma_recv_ctxt { struct ib_sge rc_recv_sge; void *rc_recv_buf; struct xdr_buf rc_arg; + bool rc_temp; u32 rc_byte_len; unsigned int rc_page_count; unsigned int rc_hdr_count; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index d4ccd1c0142c..0445e75d76a2 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -144,6 +144,7 @@ svc_rdma_recv_ctxt_alloc(struct svcxprt_rdma *rdma) ctxt->rc_recv_sge.length = rdma->sc_max_req_size; ctxt->rc_recv_sge.lkey = rdma->sc_pd->local_dma_lkey; ctxt->rc_recv_buf = buffer; + ctxt->rc_temp = false; return ctxt; fail2: @@ -154,6 +155,15 @@ fail0: return NULL; } +static void svc_rdma_recv_ctxt_destroy(struct svcxprt_rdma *rdma, + struct svc_rdma_recv_ctxt *ctxt) +{ + ib_dma_unmap_single(rdma->sc_pd->device, ctxt->rc_recv_sge.addr, + ctxt->rc_recv_sge.length, DMA_FROM_DEVICE); + kfree(ctxt->rc_recv_buf); + kfree(ctxt); +} + /** * svc_rdma_recv_ctxts_destroy - Release all recv_ctxt's for an xprt * @rdma: svcxprt_rdma being torn down @@ -165,12 +175,7 @@ void svc_rdma_recv_ctxts_destroy(struct svcxprt_rdma *rdma) while ((ctxt = svc_rdma_next_recv_ctxt(&rdma->sc_recv_ctxts))) { list_del(&ctxt->rc_list); - ib_dma_unmap_single(rdma->sc_pd->device, - ctxt->rc_recv_sge.addr, - ctxt->rc_recv_sge.length, - DMA_FROM_DEVICE); - kfree(ctxt->rc_recv_buf); - kfree(ctxt); + svc_rdma_recv_ctxt_destroy(rdma, ctxt); } } @@ -212,21 +217,21 @@ void svc_rdma_recv_ctxt_put(struct svcxprt_rdma *rdma, for (i = 0; i < ctxt->rc_page_count; i++) put_page(ctxt->rc_pages[i]); - spin_lock(&rdma->sc_recv_lock); - list_add(&ctxt->rc_list, &rdma->sc_recv_ctxts); - spin_unlock(&rdma->sc_recv_lock); + + if (!ctxt->rc_temp) { + spin_lock(&rdma->sc_recv_lock); + list_add(&ctxt->rc_list, &rdma->sc_recv_ctxts); + spin_unlock(&rdma->sc_recv_lock); + } else + svc_rdma_recv_ctxt_destroy(rdma, ctxt); } -static int svc_rdma_post_recv(struct svcxprt_rdma *rdma) +static int __svc_rdma_post_recv(struct svcxprt_rdma *rdma, + struct svc_rdma_recv_ctxt *ctxt) { - struct svc_rdma_recv_ctxt *ctxt; struct ib_recv_wr *bad_recv_wr; int ret; - ctxt = svc_rdma_recv_ctxt_get(rdma); - if (!ctxt) - return -ENOMEM; - svc_xprt_get(&rdma->sc_xprt); ret = ib_post_recv(rdma->sc_qp, &ctxt->rc_recv_wr, &bad_recv_wr); trace_svcrdma_post_recv(&ctxt->rc_recv_wr, ret); @@ -240,6 +245,16 @@ err_post: return ret; } +static int svc_rdma_post_recv(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_recv_ctxt *ctxt; + + ctxt = svc_rdma_recv_ctxt_get(rdma); + if (!ctxt) + return -ENOMEM; + return __svc_rdma_post_recv(rdma, ctxt); +} + /** * svc_rdma_post_recvs - Post initial set of Recv WRs * @rdma: fresh svcxprt_rdma @@ -248,11 +263,16 @@ err_post: */ bool svc_rdma_post_recvs(struct svcxprt_rdma *rdma) { + struct svc_rdma_recv_ctxt *ctxt; unsigned int i; int ret; for (i = 0; i < rdma->sc_max_requests; i++) { - ret = svc_rdma_post_recv(rdma); + ctxt = svc_rdma_recv_ctxt_get(rdma); + if (!ctxt) + return -ENOMEM; + ctxt->rc_temp = true; + ret = __svc_rdma_post_recv(rdma, ctxt); if (ret) { pr_err("svcrdma: failure posting recv buffers: %d\n", ret); -- cgit From f016f305f98159a9131ce200ed3b4ed92133012c Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:27:53 -0400 Subject: svcrdma: Refactor svc_rdma_dma_map_buf Clean up: svc_rdma_dma_map_buf does mostly the same thing as svc_rdma_dma_map_page, so let's fold these together. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 7 ----- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 50 ++++++++++++----------------------- 2 files changed, 17 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 27cf59c7085f..95530bc7bfab 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -158,13 +158,6 @@ struct svc_rdma_recv_ctxt { struct page *rc_pages[RPCSVC_MAXPAGES]; }; -/* Track DMA maps for this transport and context */ -static inline void svc_rdma_count_mappings(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt) -{ - ctxt->mapped_sges++; -} - /* svc_rdma_backchannel.c */ extern int svc_rdma_handle_bc_reply(struct rpc_xprt *xprt, __be32 *rdma_resp, diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index b27b597d94da..ee9ba0736ceb 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -302,41 +302,11 @@ static u32 svc_rdma_get_inv_rkey(__be32 *rdma_argp, return be32_to_cpup(p); } -/* ib_dma_map_page() is used here because svc_rdma_dma_unmap() - * is used during completion to DMA-unmap this memory, and - * it uses ib_dma_unmap_page() exclusively. - */ -static int svc_rdma_dma_map_buf(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, - unsigned int sge_no, - unsigned char *base, - unsigned int len) -{ - unsigned long offset = (unsigned long)base & ~PAGE_MASK; - struct ib_device *dev = rdma->sc_cm_id->device; - dma_addr_t dma_addr; - - dma_addr = ib_dma_map_page(dev, virt_to_page(base), - offset, len, DMA_TO_DEVICE); - if (ib_dma_mapping_error(dev, dma_addr)) - goto out_maperr; - - ctxt->sge[sge_no].addr = dma_addr; - ctxt->sge[sge_no].length = len; - ctxt->sge[sge_no].lkey = rdma->sc_pd->local_dma_lkey; - svc_rdma_count_mappings(rdma, ctxt); - return 0; - -out_maperr: - pr_err("svcrdma: failed to map buffer\n"); - return -EIO; -} - static int svc_rdma_dma_map_page(struct svcxprt_rdma *rdma, struct svc_rdma_op_ctxt *ctxt, unsigned int sge_no, struct page *page, - unsigned int offset, + unsigned long offset, unsigned int len) { struct ib_device *dev = rdma->sc_cm_id->device; @@ -349,7 +319,7 @@ static int svc_rdma_dma_map_page(struct svcxprt_rdma *rdma, ctxt->sge[sge_no].addr = dma_addr; ctxt->sge[sge_no].length = len; ctxt->sge[sge_no].lkey = rdma->sc_pd->local_dma_lkey; - svc_rdma_count_mappings(rdma, ctxt); + ctxt->mapped_sges++; return 0; out_maperr: @@ -357,6 +327,19 @@ out_maperr: return -EIO; } +/* ib_dma_map_page() is used here because svc_rdma_dma_unmap() + * handles DMA-unmap and it uses ib_dma_unmap_page() exclusively. + */ +static int svc_rdma_dma_map_buf(struct svcxprt_rdma *rdma, + struct svc_rdma_op_ctxt *ctxt, + unsigned int sge_no, + unsigned char *base, + unsigned int len) +{ + return svc_rdma_dma_map_page(rdma, ctxt, sge_no, virt_to_page(base), + offset_in_page(base), len); +} + /** * svc_rdma_map_reply_hdr - DMA map the transport header buffer * @rdma: controlling transport @@ -389,7 +372,8 @@ static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, struct svc_rdma_op_ctxt *ctxt, struct xdr_buf *xdr, __be32 *wr_lst) { - unsigned int len, sge_no, remaining, page_off; + unsigned int len, sge_no, remaining; + unsigned long page_off; struct page **ppages; unsigned char *base; u32 xdr_pad; -- cgit From 232627905f12a05df75853c62451ce0886803cee Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:27:59 -0400 Subject: svcrdma: Clean up Send SGE accounting Clean up: Since there's already a svc_rdma_op_ctxt being passed around with the running count of mapped SGEs, drop unneeded parameters to svc_rdma_post_send_wr(). Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 2 +- net/sunrpc/xprtrdma/svc_rdma_backchannel.c | 2 +- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 2 +- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 17 ++++++++--------- 4 files changed, 11 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 95530bc7bfab..8827b4e36c3c 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -188,7 +188,7 @@ extern int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, __be32 *rdma_resp, unsigned int len); extern int svc_rdma_post_send_wr(struct svcxprt_rdma *rdma, struct svc_rdma_op_ctxt *ctxt, - int num_sge, u32 inv_rkey); + u32 inv_rkey); extern int svc_rdma_sendto(struct svc_rqst *); /* svc_rdma_transport.c */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c index d50152163190..0b9ba9f50a76 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c +++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c @@ -135,7 +135,7 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma, * the rq_buffer before all retransmits are complete. */ get_page(virt_to_page(rqst->rq_buffer)); - ret = svc_rdma_post_send_wr(rdma, ctxt, 1, 0); + ret = svc_rdma_post_send_wr(rdma, ctxt, 0); if (ret) goto out_unmap; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 0445e75d76a2..af6d2f3b3242 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -639,7 +639,7 @@ static void svc_rdma_send_error(struct svcxprt_rdma *xprt, return; } - ret = svc_rdma_post_send_wr(xprt, ctxt, 1, 0); + ret = svc_rdma_post_send_wr(xprt, ctxt, 0); if (ret) { svc_rdma_unmap_dma(ctxt); svc_rdma_put_context(ctxt, 1); diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index ee9ba0736ceb..4591017adc1e 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -365,8 +365,7 @@ int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, /* Load the xdr_buf into the ctxt's sge array, and DMA map each * element as it is added. * - * Returns the number of sge elements loaded on success, or - * a negative errno on failure. + * Returns zero on success, or a negative errno on failure. */ static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, struct svc_rdma_op_ctxt *ctxt, @@ -429,7 +428,7 @@ tail: return ret; } - return sge_no - 1; + return 0; } /* The svc_rqst and all resources it owns are released as soon as @@ -453,7 +452,6 @@ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, * svc_rdma_post_send_wr - Set up and post one Send Work Request * @rdma: controlling transport * @ctxt: op_ctxt for transmitting the Send WR - * @num_sge: number of SGEs to send * @inv_rkey: R_key argument to Send With Invalidate, or zero * * Returns: @@ -463,18 +461,19 @@ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, * %-ENOMEM if ib_post_send failed. */ int svc_rdma_post_send_wr(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, int num_sge, + struct svc_rdma_op_ctxt *ctxt, u32 inv_rkey) { struct ib_send_wr *send_wr = &ctxt->send_wr; - dprintk("svcrdma: posting Send WR with %u sge(s)\n", num_sge); + dprintk("svcrdma: posting Send WR with %u sge(s)\n", + ctxt->mapped_sges); send_wr->next = NULL; ctxt->cqe.done = svc_rdma_wc_send; send_wr->wr_cqe = &ctxt->cqe; send_wr->sg_list = ctxt->sge; - send_wr->num_sge = num_sge; + send_wr->num_sge = ctxt->mapped_sges; send_wr->send_flags = IB_SEND_SIGNALED; if (inv_rkey) { send_wr->opcode = IB_WR_SEND_WITH_INV; @@ -532,7 +531,7 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, inv_rkey = 0; if (rdma->sc_snd_w_inv) inv_rkey = svc_rdma_get_inv_rkey(rdma_argp, wr_lst, rp_ch); - ret = svc_rdma_post_send_wr(rdma, ctxt, 1 + ret, inv_rkey); + ret = svc_rdma_post_send_wr(rdma, ctxt, inv_rkey); if (ret) goto err; @@ -574,7 +573,7 @@ static int svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, svc_rdma_save_io_pages(rqstp, ctxt); - ret = svc_rdma_post_send_wr(rdma, ctxt, 1 + ret, 0); + ret = svc_rdma_post_send_wr(rdma, ctxt, 0); if (ret) goto err; -- cgit From 4201c7464753827803366b40e82eb050c04ebdef Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:28:04 -0400 Subject: svcrdma: Introduce svc_rdma_send_ctxt svc_rdma_op_ctxt's are pre-allocated and maintained on a per-xprt free list. This eliminates the overhead of calling kmalloc / kfree, both of which grab a globally shared lock that disables interrupts. Introduce a replacement to svc_rdma_op_ctxt's that is built especially for the svcrdma Send path. Subsequent patches will take advantage of this new structure by allocating real resources which are then cached in these objects. The allocations are freed when the transport is torn down. I've renamed the structure so that static type checking can be used to ensure that uses of op_ctxt and send_ctxt are not confused. As an additional clean up, structure fields are renamed to conform with kernel coding conventions. Additional clean ups: - Handle svc_rdma_send_ctxt_get allocation failure at each call site, rather than pre-allocating and hoping we guessed correctly - All send_ctxt_put call-sites request page freeing, so remove the @free_pages argument - All send_ctxt_put call-sites unmap SGEs, so fold that into svc_rdma_send_ctxt_put Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 35 ++-- net/sunrpc/xprtrdma/svc_rdma_backchannel.c | 13 +- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 13 +- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 254 ++++++++++++++++++++++++----- net/sunrpc/xprtrdma/svc_rdma_transport.c | 205 +---------------------- 5 files changed, 254 insertions(+), 266 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 8827b4e36c3c..d3e2bb331264 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -109,8 +109,8 @@ struct svcxprt_rdma { struct ib_pd *sc_pd; - spinlock_t sc_ctxt_lock; - struct list_head sc_ctxts; + spinlock_t sc_send_lock; + struct list_head sc_send_ctxts; int sc_ctxt_used; spinlock_t sc_rw_ctxt_lock; struct list_head sc_rw_ctxts; @@ -158,6 +158,19 @@ struct svc_rdma_recv_ctxt { struct page *rc_pages[RPCSVC_MAXPAGES]; }; +enum { + RPCRDMA_MAX_SGES = 1 + (RPCRDMA_MAX_INLINE_THRESH / PAGE_SIZE), +}; + +struct svc_rdma_send_ctxt { + struct list_head sc_list; + struct ib_send_wr sc_send_wr; + struct ib_cqe sc_cqe; + int sc_page_count; + struct page *sc_pages[RPCSVC_MAXPAGES]; + struct ib_sge sc_sges[RPCRDMA_MAX_SGES]; +}; + /* svc_rdma_backchannel.c */ extern int svc_rdma_handle_bc_reply(struct rpc_xprt *xprt, __be32 *rdma_resp, @@ -183,24 +196,22 @@ extern int svc_rdma_send_reply_chunk(struct svcxprt_rdma *rdma, struct xdr_buf *xdr); /* svc_rdma_sendto.c */ +extern void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma); +extern struct svc_rdma_send_ctxt * + svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma); +extern void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt); +extern int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr); extern int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_send_ctxt *ctxt, __be32 *rdma_resp, unsigned int len); extern int svc_rdma_post_send_wr(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_send_ctxt *ctxt, u32 inv_rkey); extern int svc_rdma_sendto(struct svc_rqst *); /* svc_rdma_transport.c */ -extern void svc_rdma_wc_send(struct ib_cq *, struct ib_wc *); -extern void svc_rdma_wc_reg(struct ib_cq *, struct ib_wc *); -extern void svc_rdma_wc_read(struct ib_cq *, struct ib_wc *); -extern void svc_rdma_wc_inv(struct ib_cq *, struct ib_wc *); -extern int svc_rdma_send(struct svcxprt_rdma *, struct ib_send_wr *); extern int svc_rdma_create_listen(struct svc_serv *, int, struct sockaddr *); -extern struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *); -extern void svc_rdma_put_context(struct svc_rdma_op_ctxt *, int); -extern void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt); extern void svc_sq_reap(struct svcxprt_rdma *); extern void svc_rq_reap(struct svcxprt_rdma *); extern void svc_rdma_prep_reply_hdr(struct svc_rqst *); diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c index 0b9ba9f50a76..95e33511cc6f 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c +++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2015 Oracle. All rights reserved. + * Copyright (c) 2015-2018 Oracle. All rights reserved. * * Support for backward direction RPCs on RPC/RDMA (server-side). */ @@ -117,10 +117,14 @@ out_notfound: static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma, struct rpc_rqst *rqst) { - struct svc_rdma_op_ctxt *ctxt; + struct svc_rdma_send_ctxt *ctxt; int ret; - ctxt = svc_rdma_get_context(rdma); + ctxt = svc_rdma_send_ctxt_get(rdma); + if (!ctxt) { + ret = -ENOMEM; + goto out_err; + } /* rpcrdma_bc_send_request builds the transport header and * the backchannel RPC message in the same buffer. Thus only @@ -144,8 +148,7 @@ out_err: return ret; out_unmap: - svc_rdma_unmap_dma(ctxt); - svc_rdma_put_context(ctxt, 1); + svc_rdma_send_ctxt_put(rdma, ctxt); ret = -EIO; goto out_err; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index af6d2f3b3242..2d1e0db4c869 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -601,7 +601,7 @@ static void rdma_read_complete(struct svc_rqst *rqstp, static void svc_rdma_send_error(struct svcxprt_rdma *xprt, __be32 *rdma_argp, int status) { - struct svc_rdma_op_ctxt *ctxt; + struct svc_rdma_send_ctxt *ctxt; __be32 *p, *err_msgp; unsigned int length; struct page *page; @@ -631,7 +631,10 @@ static void svc_rdma_send_error(struct svcxprt_rdma *xprt, length = (unsigned long)p - (unsigned long)err_msgp; /* Map transport header; no RPC message payload */ - ctxt = svc_rdma_get_context(xprt); + ctxt = svc_rdma_send_ctxt_get(xprt); + if (!ctxt) + return; + ret = svc_rdma_map_reply_hdr(xprt, ctxt, err_msgp, length); if (ret) { dprintk("svcrdma: Error %d mapping send for protocol error\n", @@ -640,10 +643,8 @@ static void svc_rdma_send_error(struct svcxprt_rdma *xprt, } ret = svc_rdma_post_send_wr(xprt, ctxt, 0); - if (ret) { - svc_rdma_unmap_dma(ctxt); - svc_rdma_put_context(ctxt, 1); - } + if (ret) + svc_rdma_send_ctxt_put(xprt, ctxt); } /* By convention, backchannel calls arrive via rdma_msg type diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 4591017adc1e..b286d6a6e429 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -75,11 +75,11 @@ * DMA-unmap the pages under I/O for that Write segment. The Write * completion handler does not release any pages. * - * When the Send WR is constructed, it also gets its own svc_rdma_op_ctxt. + * When the Send WR is constructed, it also gets its own svc_rdma_send_ctxt. * The ownership of all of the Reply's pages are transferred into that * ctxt, the Send WR is posted, and sendto returns. * - * The svc_rdma_op_ctxt is presented when the Send WR completes. The + * The svc_rdma_send_ctxt is presented when the Send WR completes. The * Send completion handler finally releases the Reply's pages. * * This mechanism also assumes that completions on the transport's Send @@ -114,6 +114,184 @@ #define RPCDBG_FACILITY RPCDBG_SVCXPRT +static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc); + +static inline struct svc_rdma_send_ctxt * +svc_rdma_next_send_ctxt(struct list_head *list) +{ + return list_first_entry_or_null(list, struct svc_rdma_send_ctxt, + sc_list); +} + +static struct svc_rdma_send_ctxt * +svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_send_ctxt *ctxt; + int i; + + ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); + if (!ctxt) + return NULL; + + ctxt->sc_cqe.done = svc_rdma_wc_send; + ctxt->sc_send_wr.next = NULL; + ctxt->sc_send_wr.wr_cqe = &ctxt->sc_cqe; + ctxt->sc_send_wr.sg_list = ctxt->sc_sges; + ctxt->sc_send_wr.send_flags = IB_SEND_SIGNALED; + for (i = 0; i < ARRAY_SIZE(ctxt->sc_sges); i++) + ctxt->sc_sges[i].lkey = rdma->sc_pd->local_dma_lkey; + return ctxt; +} + +/** + * svc_rdma_send_ctxts_destroy - Release all send_ctxt's for an xprt + * @rdma: svcxprt_rdma being torn down + * + */ +void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_send_ctxt *ctxt; + + while ((ctxt = svc_rdma_next_send_ctxt(&rdma->sc_send_ctxts))) { + list_del(&ctxt->sc_list); + kfree(ctxt); + } +} + +/** + * svc_rdma_send_ctxt_get - Get a free send_ctxt + * @rdma: controlling svcxprt_rdma + * + * Returns a ready-to-use send_ctxt, or NULL if none are + * available and a fresh one cannot be allocated. + */ +struct svc_rdma_send_ctxt *svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma) +{ + struct svc_rdma_send_ctxt *ctxt; + + spin_lock(&rdma->sc_send_lock); + ctxt = svc_rdma_next_send_ctxt(&rdma->sc_send_ctxts); + if (!ctxt) + goto out_empty; + list_del(&ctxt->sc_list); + spin_unlock(&rdma->sc_send_lock); + +out: + ctxt->sc_send_wr.num_sge = 0; + ctxt->sc_page_count = 0; + return ctxt; + +out_empty: + spin_unlock(&rdma->sc_send_lock); + ctxt = svc_rdma_send_ctxt_alloc(rdma); + if (!ctxt) + return NULL; + goto out; +} + +/** + * svc_rdma_send_ctxt_put - Return send_ctxt to free list + * @rdma: controlling svcxprt_rdma + * @ctxt: object to return to the free list + * + * Pages left in sc_pages are DMA unmapped and released. + */ +void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt) +{ + struct ib_device *device = rdma->sc_cm_id->device; + unsigned int i; + + for (i = 0; i < ctxt->sc_send_wr.num_sge; i++) + ib_dma_unmap_page(device, + ctxt->sc_sges[i].addr, + ctxt->sc_sges[i].length, + DMA_TO_DEVICE); + + for (i = 0; i < ctxt->sc_page_count; ++i) + put_page(ctxt->sc_pages[i]); + + spin_lock(&rdma->sc_send_lock); + list_add(&ctxt->sc_list, &rdma->sc_send_ctxts); + spin_unlock(&rdma->sc_send_lock); +} + +/** + * svc_rdma_wc_send - Invoked by RDMA provider for each polled Send WC + * @cq: Completion Queue context + * @wc: Work Completion object + * + * NB: The svc_xprt/svcxprt_rdma is pinned whenever it's possible that + * the Send completion handler could be running. + */ +static void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc) +{ + struct svcxprt_rdma *rdma = cq->cq_context; + struct ib_cqe *cqe = wc->wr_cqe; + struct svc_rdma_send_ctxt *ctxt; + + trace_svcrdma_wc_send(wc); + + atomic_inc(&rdma->sc_sq_avail); + wake_up(&rdma->sc_send_wait); + + ctxt = container_of(cqe, struct svc_rdma_send_ctxt, sc_cqe); + svc_rdma_send_ctxt_put(rdma, ctxt); + + if (unlikely(wc->status != IB_WC_SUCCESS)) { + set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); + svc_xprt_enqueue(&rdma->sc_xprt); + if (wc->status != IB_WC_WR_FLUSH_ERR) + pr_err("svcrdma: Send: %s (%u/0x%x)\n", + ib_wc_status_msg(wc->status), + wc->status, wc->vendor_err); + } + + svc_xprt_put(&rdma->sc_xprt); +} + +int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr) +{ + struct ib_send_wr *bad_wr, *n_wr; + int wr_count; + int i; + int ret; + + wr_count = 1; + for (n_wr = wr->next; n_wr; n_wr = n_wr->next) + wr_count++; + + /* If the SQ is full, wait until an SQ entry is available */ + while (1) { + if ((atomic_sub_return(wr_count, &rdma->sc_sq_avail) < 0)) { + atomic_inc(&rdma_stat_sq_starve); + trace_svcrdma_sq_full(rdma); + atomic_add(wr_count, &rdma->sc_sq_avail); + wait_event(rdma->sc_send_wait, + atomic_read(&rdma->sc_sq_avail) > wr_count); + if (test_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags)) + return -ENOTCONN; + trace_svcrdma_sq_retry(rdma); + continue; + } + /* Take a transport ref for each WR posted */ + for (i = 0; i < wr_count; i++) + svc_xprt_get(&rdma->sc_xprt); + + /* Bump used SQ WR count and post */ + ret = ib_post_send(rdma->sc_qp, wr, &bad_wr); + trace_svcrdma_post_send(wr, ret); + if (ret) { + set_bit(XPT_CLOSE, &rdma->sc_xprt.xpt_flags); + for (i = 0; i < wr_count; i++) + svc_xprt_put(&rdma->sc_xprt); + wake_up(&rdma->sc_send_wait); + } + break; + } + return ret; +} + static u32 xdr_padsize(u32 len) { return (len & 3) ? (4 - (len & 3)) : 0; @@ -303,7 +481,7 @@ static u32 svc_rdma_get_inv_rkey(__be32 *rdma_argp, } static int svc_rdma_dma_map_page(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_send_ctxt *ctxt, unsigned int sge_no, struct page *page, unsigned long offset, @@ -316,10 +494,9 @@ static int svc_rdma_dma_map_page(struct svcxprt_rdma *rdma, if (ib_dma_mapping_error(dev, dma_addr)) goto out_maperr; - ctxt->sge[sge_no].addr = dma_addr; - ctxt->sge[sge_no].length = len; - ctxt->sge[sge_no].lkey = rdma->sc_pd->local_dma_lkey; - ctxt->mapped_sges++; + ctxt->sc_sges[sge_no].addr = dma_addr; + ctxt->sc_sges[sge_no].length = len; + ctxt->sc_send_wr.num_sge++; return 0; out_maperr: @@ -331,7 +508,7 @@ out_maperr: * handles DMA-unmap and it uses ib_dma_unmap_page() exclusively. */ static int svc_rdma_dma_map_buf(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_send_ctxt *ctxt, unsigned int sge_no, unsigned char *base, unsigned int len) @@ -352,14 +529,13 @@ static int svc_rdma_dma_map_buf(struct svcxprt_rdma *rdma, * %-EIO if DMA mapping failed. */ int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_send_ctxt *ctxt, __be32 *rdma_resp, unsigned int len) { - ctxt->direction = DMA_TO_DEVICE; - ctxt->pages[0] = virt_to_page(rdma_resp); - ctxt->count = 1; - return svc_rdma_dma_map_page(rdma, ctxt, 0, ctxt->pages[0], 0, len); + ctxt->sc_pages[0] = virt_to_page(rdma_resp); + ctxt->sc_page_count++; + return svc_rdma_dma_map_page(rdma, ctxt, 0, ctxt->sc_pages[0], 0, len); } /* Load the xdr_buf into the ctxt's sge array, and DMA map each @@ -368,7 +544,7 @@ int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, * Returns zero on success, or a negative errno on failure. */ static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_send_ctxt *ctxt, struct xdr_buf *xdr, __be32 *wr_lst) { unsigned int len, sge_no, remaining; @@ -436,13 +612,13 @@ tail: * so they are released by the Send completion handler. */ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, - struct svc_rdma_op_ctxt *ctxt) + struct svc_rdma_send_ctxt *ctxt) { int i, pages = rqstp->rq_next_page - rqstp->rq_respages; - ctxt->count += pages; + ctxt->sc_page_count += pages; for (i = 0; i < pages; i++) { - ctxt->pages[i + 1] = rqstp->rq_respages[i]; + ctxt->sc_pages[i + 1] = rqstp->rq_respages[i]; rqstp->rq_respages[i] = NULL; } rqstp->rq_next_page = rqstp->rq_respages + 1; @@ -461,37 +637,29 @@ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, * %-ENOMEM if ib_post_send failed. */ int svc_rdma_post_send_wr(struct svcxprt_rdma *rdma, - struct svc_rdma_op_ctxt *ctxt, + struct svc_rdma_send_ctxt *ctxt, u32 inv_rkey) { - struct ib_send_wr *send_wr = &ctxt->send_wr; - dprintk("svcrdma: posting Send WR with %u sge(s)\n", - ctxt->mapped_sges); - - send_wr->next = NULL; - ctxt->cqe.done = svc_rdma_wc_send; - send_wr->wr_cqe = &ctxt->cqe; - send_wr->sg_list = ctxt->sge; - send_wr->num_sge = ctxt->mapped_sges; - send_wr->send_flags = IB_SEND_SIGNALED; + ctxt->sc_send_wr.num_sge); + if (inv_rkey) { - send_wr->opcode = IB_WR_SEND_WITH_INV; - send_wr->ex.invalidate_rkey = inv_rkey; + ctxt->sc_send_wr.opcode = IB_WR_SEND_WITH_INV; + ctxt->sc_send_wr.ex.invalidate_rkey = inv_rkey; } else { - send_wr->opcode = IB_WR_SEND; + ctxt->sc_send_wr.opcode = IB_WR_SEND; } - return svc_rdma_send(rdma, send_wr); + return svc_rdma_send(rdma, &ctxt->sc_send_wr); } /* Prepare the portion of the RPC Reply that will be transmitted * via RDMA Send. The RPC-over-RDMA transport header is prepared - * in sge[0], and the RPC xdr_buf is prepared in following sges. + * in sc_sges[0], and the RPC xdr_buf is prepared in following sges. * * Depending on whether a Write list or Reply chunk is present, * the server may send all, a portion of, or none of the xdr_buf. - * In the latter case, only the transport header (sge[0]) is + * In the latter case, only the transport header (sc_sges[0]) is * transmitted. * * RDMA Send is the last step of transmitting an RPC reply. Pages @@ -508,11 +676,13 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, struct svc_rqst *rqstp, __be32 *wr_lst, __be32 *rp_ch) { - struct svc_rdma_op_ctxt *ctxt; + struct svc_rdma_send_ctxt *ctxt; u32 inv_rkey; int ret; - ctxt = svc_rdma_get_context(rdma); + ctxt = svc_rdma_send_ctxt_get(rdma); + if (!ctxt) + return -ENOMEM; ret = svc_rdma_map_reply_hdr(rdma, ctxt, rdma_resp, svc_rdma_reply_hdr_len(rdma_resp)); @@ -538,8 +708,7 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, return 0; err: - svc_rdma_unmap_dma(ctxt); - svc_rdma_put_context(ctxt, 1); + svc_rdma_send_ctxt_put(rdma, ctxt); return ret; } @@ -553,11 +722,13 @@ err: static int svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, __be32 *rdma_resp, struct svc_rqst *rqstp) { - struct svc_rdma_op_ctxt *ctxt; + struct svc_rdma_send_ctxt *ctxt; __be32 *p; int ret; - ctxt = svc_rdma_get_context(rdma); + ctxt = svc_rdma_send_ctxt_get(rdma); + if (!ctxt) + return -ENOMEM; /* Replace the original transport header with an * RDMA_ERROR response. XID etc are preserved. @@ -580,8 +751,7 @@ static int svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, return 0; err: - svc_rdma_unmap_dma(ctxt); - svc_rdma_put_context(ctxt, 1); + svc_rdma_send_ctxt_put(rdma, ctxt); return ret; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index baeecbb2f763..3de81735a6cc 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -1,5 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* + * Copyright (c) 2015-2018 Oracle. All rights reserved. * Copyright (c) 2014 Open Grid Computing, Inc. All rights reserved. * Copyright (c) 2005-2007 Network Appliance, Inc. All rights reserved. * @@ -157,114 +158,6 @@ static void svc_rdma_bc_free(struct svc_xprt *xprt) } #endif /* CONFIG_SUNRPC_BACKCHANNEL */ -static struct svc_rdma_op_ctxt *alloc_ctxt(struct svcxprt_rdma *xprt, - gfp_t flags) -{ - struct svc_rdma_op_ctxt *ctxt; - - ctxt = kmalloc(sizeof(*ctxt), flags); - if (ctxt) { - ctxt->xprt = xprt; - INIT_LIST_HEAD(&ctxt->list); - } - return ctxt; -} - -static bool svc_rdma_prealloc_ctxts(struct svcxprt_rdma *xprt) -{ - unsigned int i; - - i = xprt->sc_sq_depth; - while (i--) { - struct svc_rdma_op_ctxt *ctxt; - - ctxt = alloc_ctxt(xprt, GFP_KERNEL); - if (!ctxt) { - dprintk("svcrdma: No memory for RDMA ctxt\n"); - return false; - } - list_add(&ctxt->list, &xprt->sc_ctxts); - } - return true; -} - -struct svc_rdma_op_ctxt *svc_rdma_get_context(struct svcxprt_rdma *xprt) -{ - struct svc_rdma_op_ctxt *ctxt = NULL; - - spin_lock(&xprt->sc_ctxt_lock); - xprt->sc_ctxt_used++; - if (list_empty(&xprt->sc_ctxts)) - goto out_empty; - - ctxt = list_first_entry(&xprt->sc_ctxts, - struct svc_rdma_op_ctxt, list); - list_del(&ctxt->list); - spin_unlock(&xprt->sc_ctxt_lock); - -out: - ctxt->count = 0; - ctxt->mapped_sges = 0; - return ctxt; - -out_empty: - /* Either pre-allocation missed the mark, or send - * queue accounting is broken. - */ - spin_unlock(&xprt->sc_ctxt_lock); - - ctxt = alloc_ctxt(xprt, GFP_NOIO); - if (ctxt) - goto out; - - spin_lock(&xprt->sc_ctxt_lock); - xprt->sc_ctxt_used--; - spin_unlock(&xprt->sc_ctxt_lock); - WARN_ONCE(1, "svcrdma: empty RDMA ctxt list?\n"); - return NULL; -} - -void svc_rdma_unmap_dma(struct svc_rdma_op_ctxt *ctxt) -{ - struct svcxprt_rdma *xprt = ctxt->xprt; - struct ib_device *device = xprt->sc_cm_id->device; - unsigned int i; - - for (i = 0; i < ctxt->mapped_sges; i++) - ib_dma_unmap_page(device, - ctxt->sge[i].addr, - ctxt->sge[i].length, - ctxt->direction); - ctxt->mapped_sges = 0; -} - -void svc_rdma_put_context(struct svc_rdma_op_ctxt *ctxt, int free_pages) -{ - struct svcxprt_rdma *xprt = ctxt->xprt; - int i; - - if (free_pages) - for (i = 0; i < ctxt->count; i++) - put_page(ctxt->pages[i]); - - spin_lock(&xprt->sc_ctxt_lock); - xprt->sc_ctxt_used--; - list_add(&ctxt->list, &xprt->sc_ctxts); - spin_unlock(&xprt->sc_ctxt_lock); -} - -static void svc_rdma_destroy_ctxts(struct svcxprt_rdma *xprt) -{ - while (!list_empty(&xprt->sc_ctxts)) { - struct svc_rdma_op_ctxt *ctxt; - - ctxt = list_first_entry(&xprt->sc_ctxts, - struct svc_rdma_op_ctxt, list); - list_del(&ctxt->list); - kfree(ctxt); - } -} - /* QP event handler */ static void qp_event_handler(struct ib_event *event, void *context) { @@ -292,39 +185,6 @@ static void qp_event_handler(struct ib_event *event, void *context) } } -/** - * svc_rdma_wc_send - Invoked by RDMA provider for each polled Send WC - * @cq: completion queue - * @wc: completed WR - * - */ -void svc_rdma_wc_send(struct ib_cq *cq, struct ib_wc *wc) -{ - struct svcxprt_rdma *xprt = cq->cq_context; - struct ib_cqe *cqe = wc->wr_cqe; - struct svc_rdma_op_ctxt *ctxt; - - trace_svcrdma_wc_send(wc); - - atomic_inc(&xprt->sc_sq_avail); - wake_up(&xprt->sc_send_wait); - - ctxt = container_of(cqe, struct svc_rdma_op_ctxt, cqe); - svc_rdma_unmap_dma(ctxt); - svc_rdma_put_context(ctxt, 1); - - if (unlikely(wc->status != IB_WC_SUCCESS)) { - set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags); - svc_xprt_enqueue(&xprt->sc_xprt); - if (wc->status != IB_WC_WR_FLUSH_ERR) - pr_err("svcrdma: Send: %s (%u/0x%x)\n", - ib_wc_status_msg(wc->status), - wc->status, wc->vendor_err); - } - - svc_xprt_put(&xprt->sc_xprt); -} - static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, struct net *net) { @@ -338,14 +198,14 @@ static struct svcxprt_rdma *svc_rdma_create_xprt(struct svc_serv *serv, INIT_LIST_HEAD(&cma_xprt->sc_accept_q); INIT_LIST_HEAD(&cma_xprt->sc_rq_dto_q); INIT_LIST_HEAD(&cma_xprt->sc_read_complete_q); - INIT_LIST_HEAD(&cma_xprt->sc_ctxts); + INIT_LIST_HEAD(&cma_xprt->sc_send_ctxts); INIT_LIST_HEAD(&cma_xprt->sc_recv_ctxts); INIT_LIST_HEAD(&cma_xprt->sc_rw_ctxts); init_waitqueue_head(&cma_xprt->sc_send_wait); spin_lock_init(&cma_xprt->sc_lock); spin_lock_init(&cma_xprt->sc_rq_dto_lock); - spin_lock_init(&cma_xprt->sc_ctxt_lock); + spin_lock_init(&cma_xprt->sc_send_lock); spin_lock_init(&cma_xprt->sc_recv_lock); spin_lock_init(&cma_xprt->sc_rw_ctxt_lock); @@ -640,9 +500,6 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) } atomic_set(&newxprt->sc_sq_avail, newxprt->sc_sq_depth); - if (!svc_rdma_prealloc_ctxts(newxprt)) - goto errout; - newxprt->sc_pd = ib_alloc_pd(dev, 0); if (IS_ERR(newxprt->sc_pd)) { dprintk("svcrdma: error creating PD for connect request\n"); @@ -794,11 +651,6 @@ static void __svc_rdma_free(struct work_struct *work) svc_rdma_flush_recv_queues(rdma); - /* Warn if we leaked a resource or under-referenced */ - if (rdma->sc_ctxt_used != 0) - pr_err("svcrdma: ctxt still in use? (%d)\n", - rdma->sc_ctxt_used); - /* Final put of backchannel client transport */ if (xprt->xpt_bc_xprt) { xprt_put(xprt->xpt_bc_xprt); @@ -806,7 +658,7 @@ static void __svc_rdma_free(struct work_struct *work) } svc_rdma_destroy_rw_ctxts(rdma); - svc_rdma_destroy_ctxts(rdma); + svc_rdma_send_ctxts_destroy(rdma); svc_rdma_recv_ctxts_destroy(rdma); /* Destroy the QP if present (not a listener) */ @@ -860,52 +712,3 @@ static void svc_rdma_secure_port(struct svc_rqst *rqstp) static void svc_rdma_kill_temp_xprt(struct svc_xprt *xprt) { } - -int svc_rdma_send(struct svcxprt_rdma *xprt, struct ib_send_wr *wr) -{ - struct ib_send_wr *bad_wr, *n_wr; - int wr_count; - int i; - int ret; - - if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags)) - return -ENOTCONN; - - wr_count = 1; - for (n_wr = wr->next; n_wr; n_wr = n_wr->next) - wr_count++; - - /* If the SQ is full, wait until an SQ entry is available */ - while (1) { - if ((atomic_sub_return(wr_count, &xprt->sc_sq_avail) < 0)) { - atomic_inc(&rdma_stat_sq_starve); - trace_svcrdma_sq_full(xprt); - atomic_add(wr_count, &xprt->sc_sq_avail); - wait_event(xprt->sc_send_wait, - atomic_read(&xprt->sc_sq_avail) > wr_count); - if (test_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags)) - return -ENOTCONN; - trace_svcrdma_sq_retry(xprt); - continue; - } - /* Take a transport ref for each WR posted */ - for (i = 0; i < wr_count; i++) - svc_xprt_get(&xprt->sc_xprt); - - /* Bump used SQ WR count and post */ - ret = ib_post_send(xprt->sc_qp, wr, &bad_wr); - trace_svcrdma_post_send(wr, ret); - if (ret) { - set_bit(XPT_CLOSE, &xprt->sc_xprt.xpt_flags); - for (i = 0; i < wr_count; i ++) - svc_xprt_put(&xprt->sc_xprt); - dprintk("svcrdma: failed to post SQ WR rc=%d\n", ret); - dprintk(" sc_sq_avail=%d, sc_sq_depth=%d\n", - atomic_read(&xprt->sc_sq_avail), - xprt->sc_sq_depth); - wake_up(&xprt->sc_send_wait); - } - break; - } - return ret; -} -- cgit From 25fd86eca11c26bad2aede6dd4709ff58f89c7cb Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:28:09 -0400 Subject: svcrdma: Don't overrun the SGE array in svc_rdma_send_ctxt Receive buffers are always the same size, but each Send WR has a variable number of SGEs, based on the contents of the xdr_buf being sent. While assembling a Send WR, keep track of the number of SGEs so that we don't exceed the device's maximum, or walk off the end of the Send SGE array. For now the Send path just fails if it exceeds the maximum. The current logic in svc_rdma_accept bases the maximum number of Send SGEs on the largest NFS request that can be sent or received. In the transport layer, the limit is actually based on the capabilities of the underlying device, not on properties of the Upper Layer Protocol. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 9 +++----- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 36 +++++++++++++++++++------------- net/sunrpc/xprtrdma/svc_rdma_transport.c | 13 ++++++++---- 3 files changed, 33 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index d3e2bb331264..bfb8824e31e1 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -96,7 +96,7 @@ struct svcxprt_rdma { struct rdma_cm_id *sc_cm_id; /* RDMA connection id */ struct list_head sc_accept_q; /* Conn. waiting accept */ int sc_ord; /* RDMA read limit */ - int sc_max_sge; + int sc_max_send_sges; bool sc_snd_w_inv; /* OK to use Send With Invalidate */ atomic_t sc_sq_avail; /* SQEs ready to be consumed */ @@ -158,17 +158,14 @@ struct svc_rdma_recv_ctxt { struct page *rc_pages[RPCSVC_MAXPAGES]; }; -enum { - RPCRDMA_MAX_SGES = 1 + (RPCRDMA_MAX_INLINE_THRESH / PAGE_SIZE), -}; - struct svc_rdma_send_ctxt { struct list_head sc_list; struct ib_send_wr sc_send_wr; struct ib_cqe sc_cqe; int sc_page_count; + int sc_cur_sge_no; struct page *sc_pages[RPCSVC_MAXPAGES]; - struct ib_sge sc_sges[RPCRDMA_MAX_SGES]; + struct ib_sge sc_sges[]; }; /* svc_rdma_backchannel.c */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index b286d6a6e429..53d8db6bfaf2 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -127,9 +127,12 @@ static struct svc_rdma_send_ctxt * svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) { struct svc_rdma_send_ctxt *ctxt; + size_t size; int i; - ctxt = kmalloc(sizeof(*ctxt), GFP_KERNEL); + size = sizeof(*ctxt); + size += rdma->sc_max_send_sges * sizeof(struct ib_sge); + ctxt = kmalloc(size, GFP_KERNEL); if (!ctxt) return NULL; @@ -138,7 +141,7 @@ svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) ctxt->sc_send_wr.wr_cqe = &ctxt->sc_cqe; ctxt->sc_send_wr.sg_list = ctxt->sc_sges; ctxt->sc_send_wr.send_flags = IB_SEND_SIGNALED; - for (i = 0; i < ARRAY_SIZE(ctxt->sc_sges); i++) + for (i = 0; i < rdma->sc_max_send_sges; i++) ctxt->sc_sges[i].lkey = rdma->sc_pd->local_dma_lkey; return ctxt; } @@ -482,7 +485,6 @@ static u32 svc_rdma_get_inv_rkey(__be32 *rdma_argp, static int svc_rdma_dma_map_page(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt, - unsigned int sge_no, struct page *page, unsigned long offset, unsigned int len) @@ -494,8 +496,8 @@ static int svc_rdma_dma_map_page(struct svcxprt_rdma *rdma, if (ib_dma_mapping_error(dev, dma_addr)) goto out_maperr; - ctxt->sc_sges[sge_no].addr = dma_addr; - ctxt->sc_sges[sge_no].length = len; + ctxt->sc_sges[ctxt->sc_cur_sge_no].addr = dma_addr; + ctxt->sc_sges[ctxt->sc_cur_sge_no].length = len; ctxt->sc_send_wr.num_sge++; return 0; @@ -509,11 +511,10 @@ out_maperr: */ static int svc_rdma_dma_map_buf(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt, - unsigned int sge_no, unsigned char *base, unsigned int len) { - return svc_rdma_dma_map_page(rdma, ctxt, sge_no, virt_to_page(base), + return svc_rdma_dma_map_page(rdma, ctxt, virt_to_page(base), offset_in_page(base), len); } @@ -535,7 +536,8 @@ int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, { ctxt->sc_pages[0] = virt_to_page(rdma_resp); ctxt->sc_page_count++; - return svc_rdma_dma_map_page(rdma, ctxt, 0, ctxt->sc_pages[0], 0, len); + ctxt->sc_cur_sge_no = 0; + return svc_rdma_dma_map_page(rdma, ctxt, ctxt->sc_pages[0], 0, len); } /* Load the xdr_buf into the ctxt's sge array, and DMA map each @@ -547,16 +549,16 @@ static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt, struct xdr_buf *xdr, __be32 *wr_lst) { - unsigned int len, sge_no, remaining; + unsigned int len, remaining; unsigned long page_off; struct page **ppages; unsigned char *base; u32 xdr_pad; int ret; - sge_no = 1; - - ret = svc_rdma_dma_map_buf(rdma, ctxt, sge_no++, + if (++ctxt->sc_cur_sge_no >= rdma->sc_max_send_sges) + return -EIO; + ret = svc_rdma_dma_map_buf(rdma, ctxt, xdr->head[0].iov_base, xdr->head[0].iov_len); if (ret < 0) @@ -586,8 +588,10 @@ static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, while (remaining) { len = min_t(u32, PAGE_SIZE - page_off, remaining); - ret = svc_rdma_dma_map_page(rdma, ctxt, sge_no++, - *ppages++, page_off, len); + if (++ctxt->sc_cur_sge_no >= rdma->sc_max_send_sges) + return -EIO; + ret = svc_rdma_dma_map_page(rdma, ctxt, *ppages++, + page_off, len); if (ret < 0) return ret; @@ -599,7 +603,9 @@ static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, len = xdr->tail[0].iov_len; tail: if (len) { - ret = svc_rdma_dma_map_buf(rdma, ctxt, sge_no++, base, len); + if (++ctxt->sc_cur_sge_no >= rdma->sc_max_send_sges) + return -EIO; + ret = svc_rdma_dma_map_buf(rdma, ctxt, base, len); if (ret < 0) return ret; } diff --git a/net/sunrpc/xprtrdma/svc_rdma_transport.c b/net/sunrpc/xprtrdma/svc_rdma_transport.c index 3de81735a6cc..e9535a66bab0 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_transport.c +++ b/net/sunrpc/xprtrdma/svc_rdma_transport.c @@ -476,8 +476,13 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) /* Qualify the transport resource defaults with the * capabilities of this particular device */ - newxprt->sc_max_sge = min((size_t)dev->attrs.max_sge, - (size_t)RPCSVC_MAXPAGES); + newxprt->sc_max_send_sges = dev->attrs.max_sge; + /* transport hdr, head iovec, one page list entry, tail iovec */ + if (newxprt->sc_max_send_sges < 4) { + pr_err("svcrdma: too few Send SGEs available (%d)\n", + newxprt->sc_max_send_sges); + goto errout; + } newxprt->sc_max_req_size = svcrdma_max_req_size; newxprt->sc_max_requests = svcrdma_max_requests; newxprt->sc_max_bc_requests = svcrdma_max_bc_requests; @@ -525,7 +530,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) qp_attr.cap.max_rdma_ctxs = ctxts; qp_attr.cap.max_send_wr = newxprt->sc_sq_depth - ctxts; qp_attr.cap.max_recv_wr = rq_depth; - qp_attr.cap.max_send_sge = newxprt->sc_max_sge; + qp_attr.cap.max_send_sge = newxprt->sc_max_send_sges; qp_attr.cap.max_recv_sge = 1; qp_attr.sq_sig_type = IB_SIGNAL_REQ_WR; qp_attr.qp_type = IB_QPT_RC; @@ -586,7 +591,7 @@ static struct svc_xprt *svc_rdma_accept(struct svc_xprt *xprt) dprintk(" local address : %pIS:%u\n", sap, rpc_get_port(sap)); sap = (struct sockaddr *)&newxprt->sc_cm_id->route.addr.dst_addr; dprintk(" remote address : %pIS:%u\n", sap, rpc_get_port(sap)); - dprintk(" max_sge : %d\n", newxprt->sc_max_sge); + dprintk(" max_sge : %d\n", newxprt->sc_max_send_sges); dprintk(" sq_depth : %d\n", newxprt->sc_sq_depth); dprintk(" rdma_rw_ctxs : %d\n", ctxts); dprintk(" max_requests : %d\n", newxprt->sc_max_requests); -- cgit From 986b78894b268f605e9ea055b99959bdce0e5945 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:28:15 -0400 Subject: svcrdma: Remove post_send_wr Clean up: Now that the send_wr is part of the svc_rdma_send_ctxt, svc_rdma_post_send_wr is nearly empty. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 3 -- net/sunrpc/xprtrdma/svc_rdma_backchannel.c | 3 +- net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 3 +- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 47 ++++++++---------------------- 4 files changed, 16 insertions(+), 40 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index bfb8824e31e1..a8bfc214614b 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -202,9 +202,6 @@ extern int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr); extern int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt, __be32 *rdma_resp, unsigned int len); -extern int svc_rdma_post_send_wr(struct svcxprt_rdma *rdma, - struct svc_rdma_send_ctxt *ctxt, - u32 inv_rkey); extern int svc_rdma_sendto(struct svc_rqst *); /* svc_rdma_transport.c */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c index 95e33511cc6f..40f5e4afbcc8 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c +++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c @@ -139,7 +139,8 @@ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma, * the rq_buffer before all retransmits are complete. */ get_page(virt_to_page(rqst->rq_buffer)); - ret = svc_rdma_post_send_wr(rdma, ctxt, 0); + ctxt->sc_send_wr.opcode = IB_WR_SEND; + ret = svc_rdma_send(rdma, &ctxt->sc_send_wr); if (ret) goto out_unmap; diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 2d1e0db4c869..68648e6c5be2 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -642,7 +642,8 @@ static void svc_rdma_send_error(struct svcxprt_rdma *xprt, return; } - ret = svc_rdma_post_send_wr(xprt, ctxt, 0); + ctxt->sc_send_wr.opcode = IB_WR_SEND; + ret = svc_rdma_send(xprt, &ctxt->sc_send_wr); if (ret) svc_rdma_send_ctxt_put(xprt, ctxt); } diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index 53d8db6bfaf2..0ebdc0c76483 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -630,35 +630,6 @@ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, rqstp->rq_next_page = rqstp->rq_respages + 1; } -/** - * svc_rdma_post_send_wr - Set up and post one Send Work Request - * @rdma: controlling transport - * @ctxt: op_ctxt for transmitting the Send WR - * @inv_rkey: R_key argument to Send With Invalidate, or zero - * - * Returns: - * %0 if the Send* was posted successfully, - * %-ENOTCONN if the connection was lost or dropped, - * %-EINVAL if there was a problem with the Send we built, - * %-ENOMEM if ib_post_send failed. - */ -int svc_rdma_post_send_wr(struct svcxprt_rdma *rdma, - struct svc_rdma_send_ctxt *ctxt, - u32 inv_rkey) -{ - dprintk("svcrdma: posting Send WR with %u sge(s)\n", - ctxt->sc_send_wr.num_sge); - - if (inv_rkey) { - ctxt->sc_send_wr.opcode = IB_WR_SEND_WITH_INV; - ctxt->sc_send_wr.ex.invalidate_rkey = inv_rkey; - } else { - ctxt->sc_send_wr.opcode = IB_WR_SEND; - } - - return svc_rdma_send(rdma, &ctxt->sc_send_wr); -} - /* Prepare the portion of the RPC Reply that will be transmitted * via RDMA Send. The RPC-over-RDMA transport header is prepared * in sc_sges[0], and the RPC xdr_buf is prepared in following sges. @@ -683,7 +654,6 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, __be32 *wr_lst, __be32 *rp_ch) { struct svc_rdma_send_ctxt *ctxt; - u32 inv_rkey; int ret; ctxt = svc_rdma_send_ctxt_get(rdma); @@ -704,10 +674,16 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, svc_rdma_save_io_pages(rqstp, ctxt); - inv_rkey = 0; - if (rdma->sc_snd_w_inv) - inv_rkey = svc_rdma_get_inv_rkey(rdma_argp, wr_lst, rp_ch); - ret = svc_rdma_post_send_wr(rdma, ctxt, inv_rkey); + ctxt->sc_send_wr.opcode = IB_WR_SEND; + if (rdma->sc_snd_w_inv) { + ctxt->sc_send_wr.ex.invalidate_rkey = + svc_rdma_get_inv_rkey(rdma_argp, wr_lst, rp_ch); + if (ctxt->sc_send_wr.ex.invalidate_rkey) + ctxt->sc_send_wr.opcode = IB_WR_SEND_WITH_INV; + } + dprintk("svcrdma: posting Send WR with %u sge(s)\n", + ctxt->sc_send_wr.num_sge); + ret = svc_rdma_send(rdma, &ctxt->sc_send_wr); if (ret) goto err; @@ -750,7 +726,8 @@ static int svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, svc_rdma_save_io_pages(rqstp, ctxt); - ret = svc_rdma_post_send_wr(rdma, ctxt, 0); + ctxt->sc_send_wr.opcode = IB_WR_SEND; + ret = svc_rdma_send(rdma, &ctxt->sc_send_wr); if (ret) goto err; -- cgit From 99722fe4d5a634707ced8d8f42b883b87a86b3c5 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:28:25 -0400 Subject: svcrdma: Persistently allocate and DMA-map Send buffers While sending each RPC Reply, svc_rdma_sendto allocates and DMA- maps a separate buffer where the RPC/RDMA transport header is constructed. The buffer is unmapped and released in the Send completion handler. This is significant per-RPC overhead, especially for small RPCs. Instead, allocate and DMA-map a buffer, and cache it in each svc_rdma_send_ctxt. This buffer and its mapping can be re-used for each RPC, saving the cost of memory allocation and DMA mapping. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 8 +- net/sunrpc/xprtrdma/svc_rdma_backchannel.c | 51 ++++------ net/sunrpc/xprtrdma/svc_rdma_recvfrom.c | 25 ++--- net/sunrpc/xprtrdma/svc_rdma_sendto.c | 149 +++++++++++++++-------------- 4 files changed, 105 insertions(+), 128 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index a8bfc214614b..96b14a72d359 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -162,6 +162,7 @@ struct svc_rdma_send_ctxt { struct list_head sc_list; struct ib_send_wr sc_send_wr; struct ib_cqe sc_cqe; + void *sc_xprt_buf; int sc_page_count; int sc_cur_sge_no; struct page *sc_pages[RPCSVC_MAXPAGES]; @@ -199,9 +200,12 @@ extern struct svc_rdma_send_ctxt * extern void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt); extern int svc_rdma_send(struct svcxprt_rdma *rdma, struct ib_send_wr *wr); -extern int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, +extern void svc_rdma_sync_reply_hdr(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt, + unsigned int len); +extern int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, struct svc_rdma_send_ctxt *ctxt, - __be32 *rdma_resp, unsigned int len); + struct xdr_buf *xdr, __be32 *wr_lst); extern int svc_rdma_sendto(struct svc_rqst *); /* svc_rdma_transport.c */ diff --git a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c index 40f5e4afbcc8..343e7add672c 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_backchannel.c +++ b/net/sunrpc/xprtrdma/svc_rdma_backchannel.c @@ -115,43 +115,21 @@ out_notfound: * the adapter has a small maximum SQ depth. */ static int svc_rdma_bc_sendto(struct svcxprt_rdma *rdma, - struct rpc_rqst *rqst) + struct rpc_rqst *rqst, + struct svc_rdma_send_ctxt *ctxt) { - struct svc_rdma_send_ctxt *ctxt; int ret; - ctxt = svc_rdma_send_ctxt_get(rdma); - if (!ctxt) { - ret = -ENOMEM; - goto out_err; - } - - /* rpcrdma_bc_send_request builds the transport header and - * the backchannel RPC message in the same buffer. Thus only - * one SGE is needed to send both. - */ - ret = svc_rdma_map_reply_hdr(rdma, ctxt, rqst->rq_buffer, - rqst->rq_snd_buf.len); + ret = svc_rdma_map_reply_msg(rdma, ctxt, &rqst->rq_snd_buf, NULL); if (ret < 0) - goto out_err; + return -EIO; /* Bump page refcnt so Send completion doesn't release * the rq_buffer before all retransmits are complete. */ get_page(virt_to_page(rqst->rq_buffer)); ctxt->sc_send_wr.opcode = IB_WR_SEND; - ret = svc_rdma_send(rdma, &ctxt->sc_send_wr); - if (ret) - goto out_unmap; - -out_err: - dprintk("svcrdma: %s returns %d\n", __func__, ret); - return ret; - -out_unmap: - svc_rdma_send_ctxt_put(rdma, ctxt); - ret = -EIO; - goto out_err; + return svc_rdma_send(rdma, &ctxt->sc_send_wr); } /* Server-side transport endpoint wants a whole page for its send @@ -198,13 +176,15 @@ rpcrdma_bc_send_request(struct svcxprt_rdma *rdma, struct rpc_rqst *rqst) { struct rpc_xprt *xprt = rqst->rq_xprt; struct rpcrdma_xprt *r_xprt = rpcx_to_rdmax(xprt); + struct svc_rdma_send_ctxt *ctxt; __be32 *p; int rc; - /* Space in the send buffer for an RPC/RDMA header is reserved - * via xprt->tsh_size. - */ - p = rqst->rq_buffer; + ctxt = svc_rdma_send_ctxt_get(rdma); + if (!ctxt) + goto drop_connection; + + p = ctxt->sc_xprt_buf; *p++ = rqst->rq_xid; *p++ = rpcrdma_version; *p++ = cpu_to_be32(r_xprt->rx_buf.rb_bc_max_requests); @@ -212,14 +192,17 @@ rpcrdma_bc_send_request(struct svcxprt_rdma *rdma, struct rpc_rqst *rqst) *p++ = xdr_zero; *p++ = xdr_zero; *p = xdr_zero; + svc_rdma_sync_reply_hdr(rdma, ctxt, RPCRDMA_HDRLEN_MIN); #ifdef SVCRDMA_BACKCHANNEL_DEBUG pr_info("%s: %*ph\n", __func__, 64, rqst->rq_buffer); #endif - rc = svc_rdma_bc_sendto(rdma, rqst); - if (rc) + rc = svc_rdma_bc_sendto(rdma, rqst, ctxt); + if (rc) { + svc_rdma_send_ctxt_put(rdma, ctxt); goto drop_connection; + } return rc; drop_connection: @@ -327,7 +310,7 @@ xprt_setup_rdma_bc(struct xprt_create *args) xprt->idle_timeout = RPCRDMA_IDLE_DISC_TO; xprt->prot = XPRT_TRANSPORT_BC_RDMA; - xprt->tsh_size = RPCRDMA_HDRLEN_MIN / sizeof(__be32); + xprt->tsh_size = 0; xprt->ops = &xprt_rdma_bc_procs; memcpy(&xprt->addr, args->dstaddr, args->addrlen); diff --git a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c index 68648e6c5be2..09ce09b3ac6e 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c +++ b/net/sunrpc/xprtrdma/svc_rdma_recvfrom.c @@ -602,17 +602,15 @@ static void svc_rdma_send_error(struct svcxprt_rdma *xprt, __be32 *rdma_argp, int status) { struct svc_rdma_send_ctxt *ctxt; - __be32 *p, *err_msgp; unsigned int length; - struct page *page; + __be32 *p; int ret; - page = alloc_page(GFP_KERNEL); - if (!page) + ctxt = svc_rdma_send_ctxt_get(xprt); + if (!ctxt) return; - err_msgp = page_address(page); - p = err_msgp; + p = ctxt->sc_xprt_buf; *p++ = *rdma_argp; *p++ = *(rdma_argp + 1); *p++ = xprt->sc_fc_credits; @@ -628,19 +626,8 @@ static void svc_rdma_send_error(struct svcxprt_rdma *xprt, *p++ = err_chunk; trace_svcrdma_err_chunk(*rdma_argp); } - length = (unsigned long)p - (unsigned long)err_msgp; - - /* Map transport header; no RPC message payload */ - ctxt = svc_rdma_send_ctxt_get(xprt); - if (!ctxt) - return; - - ret = svc_rdma_map_reply_hdr(xprt, ctxt, err_msgp, length); - if (ret) { - dprintk("svcrdma: Error %d mapping send for protocol error\n", - ret); - return; - } + length = (unsigned long)p - (unsigned long)ctxt->sc_xprt_buf; + svc_rdma_sync_reply_hdr(xprt, ctxt, length); ctxt->sc_send_wr.opcode = IB_WR_SEND; ret = svc_rdma_send(xprt, &ctxt->sc_send_wr); diff --git a/net/sunrpc/xprtrdma/svc_rdma_sendto.c b/net/sunrpc/xprtrdma/svc_rdma_sendto.c index edfeca45ac1c..4a3efaea277c 100644 --- a/net/sunrpc/xprtrdma/svc_rdma_sendto.c +++ b/net/sunrpc/xprtrdma/svc_rdma_sendto.c @@ -127,6 +127,8 @@ static struct svc_rdma_send_ctxt * svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) { struct svc_rdma_send_ctxt *ctxt; + dma_addr_t addr; + void *buffer; size_t size; int i; @@ -134,16 +136,33 @@ svc_rdma_send_ctxt_alloc(struct svcxprt_rdma *rdma) size += rdma->sc_max_send_sges * sizeof(struct ib_sge); ctxt = kmalloc(size, GFP_KERNEL); if (!ctxt) - return NULL; + goto fail0; + buffer = kmalloc(rdma->sc_max_req_size, GFP_KERNEL); + if (!buffer) + goto fail1; + addr = ib_dma_map_single(rdma->sc_pd->device, buffer, + rdma->sc_max_req_size, DMA_TO_DEVICE); + if (ib_dma_mapping_error(rdma->sc_pd->device, addr)) + goto fail2; - ctxt->sc_cqe.done = svc_rdma_wc_send; ctxt->sc_send_wr.next = NULL; ctxt->sc_send_wr.wr_cqe = &ctxt->sc_cqe; ctxt->sc_send_wr.sg_list = ctxt->sc_sges; ctxt->sc_send_wr.send_flags = IB_SEND_SIGNALED; + ctxt->sc_cqe.done = svc_rdma_wc_send; + ctxt->sc_xprt_buf = buffer; + ctxt->sc_sges[0].addr = addr; + for (i = 0; i < rdma->sc_max_send_sges; i++) ctxt->sc_sges[i].lkey = rdma->sc_pd->local_dma_lkey; return ctxt; + +fail2: + kfree(buffer); +fail1: + kfree(ctxt); +fail0: + return NULL; } /** @@ -157,6 +176,11 @@ void svc_rdma_send_ctxts_destroy(struct svcxprt_rdma *rdma) while ((ctxt = svc_rdma_next_send_ctxt(&rdma->sc_send_ctxts))) { list_del(&ctxt->sc_list); + ib_dma_unmap_single(rdma->sc_pd->device, + ctxt->sc_sges[0].addr, + rdma->sc_max_req_size, + DMA_TO_DEVICE); + kfree(ctxt->sc_xprt_buf); kfree(ctxt); } } @@ -181,6 +205,7 @@ struct svc_rdma_send_ctxt *svc_rdma_send_ctxt_get(struct svcxprt_rdma *rdma) out: ctxt->sc_send_wr.num_sge = 0; + ctxt->sc_cur_sge_no = 0; ctxt->sc_page_count = 0; return ctxt; @@ -205,7 +230,10 @@ void svc_rdma_send_ctxt_put(struct svcxprt_rdma *rdma, struct ib_device *device = rdma->sc_cm_id->device; unsigned int i; - for (i = 0; i < ctxt->sc_send_wr.num_sge; i++) + /* The first SGE contains the transport header, which + * remains mapped until @ctxt is destroyed. + */ + for (i = 1; i < ctxt->sc_send_wr.num_sge; i++) ib_dma_unmap_page(device, ctxt->sc_sges[i].addr, ctxt->sc_sges[i].length, @@ -519,35 +547,37 @@ static int svc_rdma_dma_map_buf(struct svcxprt_rdma *rdma, } /** - * svc_rdma_map_reply_hdr - DMA map the transport header buffer + * svc_rdma_sync_reply_hdr - DMA sync the transport header buffer * @rdma: controlling transport - * @ctxt: op_ctxt for the Send WR - * @rdma_resp: buffer containing transport header + * @ctxt: send_ctxt for the Send WR * @len: length of transport header * - * Returns: - * %0 if the header is DMA mapped, - * %-EIO if DMA mapping failed. */ -int svc_rdma_map_reply_hdr(struct svcxprt_rdma *rdma, - struct svc_rdma_send_ctxt *ctxt, - __be32 *rdma_resp, - unsigned int len) +void svc_rdma_sync_reply_hdr(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt, + unsigned int len) { - ctxt->sc_pages[0] = virt_to_page(rdma_resp); - ctxt->sc_page_count++; - ctxt->sc_cur_sge_no = 0; - return svc_rdma_dma_map_page(rdma, ctxt, ctxt->sc_pages[0], 0, len); + ctxt->sc_sges[0].length = len; + ctxt->sc_send_wr.num_sge++; + ib_dma_sync_single_for_device(rdma->sc_pd->device, + ctxt->sc_sges[0].addr, len, + DMA_TO_DEVICE); } -/* Load the xdr_buf into the ctxt's sge array, and DMA map each +/* svc_rdma_map_reply_msg - Map the buffer holding RPC message + * @rdma: controlling transport + * @ctxt: send_ctxt for the Send WR + * @xdr: prepared xdr_buf containing RPC message + * @wr_lst: pointer to Call header's Write list, or NULL + * + * Load the xdr_buf into the ctxt's sge array, and DMA map each * element as it is added. * * Returns zero on success, or a negative errno on failure. */ -static int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, - struct svc_rdma_send_ctxt *ctxt, - struct xdr_buf *xdr, __be32 *wr_lst) +int svc_rdma_map_reply_msg(struct svcxprt_rdma *rdma, + struct svc_rdma_send_ctxt *ctxt, + struct xdr_buf *xdr, __be32 *wr_lst) { unsigned int len, remaining; unsigned long page_off; @@ -624,7 +654,7 @@ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, ctxt->sc_page_count += pages; for (i = 0; i < pages; i++) { - ctxt->sc_pages[i + 1] = rqstp->rq_respages[i]; + ctxt->sc_pages[i] = rqstp->rq_respages[i]; rqstp->rq_respages[i] = NULL; } rqstp->rq_next_page = rqstp->rq_respages + 1; @@ -649,27 +679,18 @@ static void svc_rdma_save_io_pages(struct svc_rqst *rqstp, * - The Reply's transport header will never be larger than a page. */ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, - __be32 *rdma_argp, __be32 *rdma_resp, + struct svc_rdma_send_ctxt *ctxt, + __be32 *rdma_argp, struct svc_rqst *rqstp, __be32 *wr_lst, __be32 *rp_ch) { - struct svc_rdma_send_ctxt *ctxt; int ret; - ctxt = svc_rdma_send_ctxt_get(rdma); - if (!ctxt) - return -ENOMEM; - - ret = svc_rdma_map_reply_hdr(rdma, ctxt, rdma_resp, - svc_rdma_reply_hdr_len(rdma_resp)); - if (ret < 0) - goto err; - if (!rp_ch) { ret = svc_rdma_map_reply_msg(rdma, ctxt, &rqstp->rq_res, wr_lst); if (ret < 0) - goto err; + return ret; } svc_rdma_save_io_pages(rqstp, ctxt); @@ -683,15 +704,7 @@ static int svc_rdma_send_reply_msg(struct svcxprt_rdma *rdma, } dprintk("svcrdma: posting Send WR with %u sge(s)\n", ctxt->sc_send_wr.num_sge); - ret = svc_rdma_send(rdma, &ctxt->sc_send_wr); - if (ret) - goto err; - - return 0; - -err: - svc_rdma_send_ctxt_put(rdma, ctxt); - return ret; + return svc_rdma_send(rdma, &ctxt->sc_send_wr); } /* Given the client-provided Write and Reply chunks, the server was not @@ -702,40 +715,29 @@ err: * Remote Invalidation is skipped for simplicity. */ static int svc_rdma_send_error_msg(struct svcxprt_rdma *rdma, - __be32 *rdma_resp, struct svc_rqst *rqstp) + struct svc_rdma_send_ctxt *ctxt, + struct svc_rqst *rqstp) { - struct svc_rdma_send_ctxt *ctxt; __be32 *p; int ret; - ctxt = svc_rdma_send_ctxt_get(rdma); - if (!ctxt) - return -ENOMEM; - - /* Replace the original transport header with an - * RDMA_ERROR response. XID etc are preserved. - */ - trace_svcrdma_err_chunk(*rdma_resp); - p = rdma_resp + 3; + p = ctxt->sc_xprt_buf; + trace_svcrdma_err_chunk(*p); + p += 3; *p++ = rdma_error; *p = err_chunk; - - ret = svc_rdma_map_reply_hdr(rdma, ctxt, rdma_resp, 20); - if (ret < 0) - goto err; + svc_rdma_sync_reply_hdr(rdma, ctxt, RPCRDMA_HDRLEN_ERR); svc_rdma_save_io_pages(rqstp, ctxt); ctxt->sc_send_wr.opcode = IB_WR_SEND; ret = svc_rdma_send(rdma, &ctxt->sc_send_wr); - if (ret) - goto err; + if (ret) { + svc_rdma_send_ctxt_put(rdma, ctxt); + return ret; + } return 0; - -err: - svc_rdma_send_ctxt_put(rdma, ctxt); - return ret; } void svc_rdma_prep_reply_hdr(struct svc_rqst *rqstp) @@ -762,7 +764,7 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) struct svc_rdma_recv_ctxt *rctxt = rqstp->rq_xprt_ctxt; __be32 *p, *rdma_argp, *rdma_resp, *wr_lst, *rp_ch; struct xdr_buf *xdr = &rqstp->rq_res; - struct page *res_page; + struct svc_rdma_send_ctxt *sctxt; int ret; rdma_argp = rctxt->rc_recv_buf; @@ -775,10 +777,10 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) * critical section. */ ret = -ENOMEM; - res_page = alloc_page(GFP_KERNEL); - if (!res_page) + sctxt = svc_rdma_send_ctxt_get(rdma); + if (!sctxt) goto err0; - rdma_resp = page_address(res_page); + rdma_resp = sctxt->sc_xprt_buf; p = rdma_resp; *p++ = *rdma_argp; @@ -805,10 +807,11 @@ int svc_rdma_sendto(struct svc_rqst *rqstp) svc_rdma_xdr_encode_reply_chunk(rdma_resp, rp_ch, ret); } - ret = svc_rdma_send_reply_msg(rdma, rdma_argp, rdma_resp, rqstp, + svc_rdma_sync_reply_hdr(rdma, sctxt, svc_rdma_reply_hdr_len(rdma_resp)); + ret = svc_rdma_send_reply_msg(rdma, sctxt, rdma_argp, rqstp, wr_lst, rp_ch); if (ret < 0) - goto err0; + goto err1; ret = 0; out: @@ -820,14 +823,14 @@ out: if (ret != -E2BIG && ret != -EINVAL) goto err1; - ret = svc_rdma_send_error_msg(rdma, rdma_resp, rqstp); + ret = svc_rdma_send_error_msg(rdma, sctxt, rqstp); if (ret < 0) - goto err0; + goto err1; ret = 0; goto out; err1: - put_page(res_page); + svc_rdma_send_ctxt_put(rdma, sctxt); err0: trace_svcrdma_send_failed(rqstp, ret); set_bit(XPT_CLOSE, &xprt->xpt_flags); -- cgit From 51cc257a1186f69dbb6faec1bd48cc7e1fc31079 Mon Sep 17 00:00:00 2001 From: Chuck Lever Date: Mon, 7 May 2018 15:28:31 -0400 Subject: svcrdma: Remove unused svc_rdma_op_ctxt Clean up: Eliminate a structure that is no longer used. Signed-off-by: Chuck Lever Signed-off-by: J. Bruce Fields --- include/linux/sunrpc/svc_rdma.h | 21 --------------------- 1 file changed, 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sunrpc/svc_rdma.h b/include/linux/sunrpc/svc_rdma.h index 96b14a72d359..fd78f78df5c6 100644 --- a/include/linux/sunrpc/svc_rdma.h +++ b/include/linux/sunrpc/svc_rdma.h @@ -71,26 +71,6 @@ extern atomic_t rdma_stat_rq_prod; extern atomic_t rdma_stat_sq_poll; extern atomic_t rdma_stat_sq_prod; -/* - * Contexts are built when an RDMA request is created and are a - * record of the resources that can be recovered when the request - * completes. - */ -struct svc_rdma_op_ctxt { - struct list_head list; - struct xdr_buf arg; - struct ib_cqe cqe; - u32 byte_len; - struct svcxprt_rdma *xprt; - enum dma_data_direction direction; - int count; - unsigned int mapped_sges; - int hdr_count; - struct ib_send_wr send_wr; - struct ib_sge sge[1 + RPCRDMA_MAX_INLINE_THRESH / PAGE_SIZE]; - struct page *pages[RPCSVC_MAXPAGES]; -}; - struct svcxprt_rdma { struct svc_xprt sc_xprt; /* SVC transport structure */ struct rdma_cm_id *sc_cm_id; /* RDMA connection id */ @@ -111,7 +91,6 @@ struct svcxprt_rdma { spinlock_t sc_send_lock; struct list_head sc_send_ctxts; - int sc_ctxt_used; spinlock_t sc_rw_ctxt_lock; struct list_head sc_rw_ctxts; -- cgit From c06c4d793584b965bf5fa3fb107f6279643574e2 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 May 2018 20:12:23 -0400 Subject: staging: iio: tsl2x7x/tsl2772: move out of staging Move the tsl2772 driver out of staging and into mainline. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/iio/light/Kconfig | 8 + drivers/iio/light/Makefile | 1 + drivers/iio/light/tsl2772.c | 1800 +++++++++++++++++++++++++++++++++ drivers/staging/iio/Kconfig | 1 - drivers/staging/iio/Makefile | 1 - drivers/staging/iio/light/Kconfig | 14 - drivers/staging/iio/light/Makefile | 5 - drivers/staging/iio/light/tsl2772.c | 1800 --------------------------------- drivers/staging/iio/light/tsl2772.h | 101 -- include/linux/platform_data/tsl2772.h | 101 ++ 10 files changed, 1910 insertions(+), 1922 deletions(-) create mode 100644 drivers/iio/light/tsl2772.c delete mode 100644 drivers/staging/iio/light/Kconfig delete mode 100644 drivers/staging/iio/light/Makefile delete mode 100644 drivers/staging/iio/light/tsl2772.c delete mode 100644 drivers/staging/iio/light/tsl2772.h create mode 100644 include/linux/platform_data/tsl2772.h (limited to 'include/linux') diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 074e50657366..c7ef8d1862d6 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -409,6 +409,14 @@ config TSL2583 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. Access ALS data via iio, sysfs. +config TSL2772 + tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" + depends on I2C + help + Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, + tmd2672, tsl2772, tmd2772 devices. + Provides iio_events and direct access via sysfs. + config TSL4531 tristate "TAOS TSL4531 ambient light sensors" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index f1777036d4f8..80943af5d627 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TSL2583) += tsl2583.o +obj-$(CONFIG_TSL2772) += tsl2772.o obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o obj-$(CONFIG_VCNL4000) += vcnl4000.o diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c new file mode 100644 index 000000000000..34d42a2504c9 --- /dev/null +++ b/drivers/iio/light/tsl2772.c @@ -0,0 +1,1800 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Device driver for monitoring ambient light intensity in (lux) and proximity + * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771, + * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices. + * + * Copyright (c) 2012, TAOS Corporation. + * Copyright (c) 2017-2018 Brian Masney + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Cal defs */ +#define PROX_STAT_CAL 0 +#define PROX_STAT_SAMP 1 +#define MAX_SAMPLES_CAL 200 + +/* TSL2772 Device ID */ +#define TRITON_ID 0x00 +#define SWORDFISH_ID 0x30 +#define HALIBUT_ID 0x20 + +/* Lux calculation constants */ +#define TSL2772_LUX_CALC_OVER_FLOW 65535 + +/* + * TAOS Register definitions - Note: depending on device, some of these register + * are not used and the register address is benign. + */ + +/* Register offsets */ +#define TSL2772_MAX_CONFIG_REG 16 + +/* Device Registers and Masks */ +#define TSL2772_CNTRL 0x00 +#define TSL2772_ALS_TIME 0X01 +#define TSL2772_PRX_TIME 0x02 +#define TSL2772_WAIT_TIME 0x03 +#define TSL2772_ALS_MINTHRESHLO 0X04 +#define TSL2772_ALS_MINTHRESHHI 0X05 +#define TSL2772_ALS_MAXTHRESHLO 0X06 +#define TSL2772_ALS_MAXTHRESHHI 0X07 +#define TSL2772_PRX_MINTHRESHLO 0X08 +#define TSL2772_PRX_MINTHRESHHI 0X09 +#define TSL2772_PRX_MAXTHRESHLO 0X0A +#define TSL2772_PRX_MAXTHRESHHI 0X0B +#define TSL2772_PERSISTENCE 0x0C +#define TSL2772_ALS_PRX_CONFIG 0x0D +#define TSL2772_PRX_COUNT 0x0E +#define TSL2772_GAIN 0x0F +#define TSL2772_NOTUSED 0x10 +#define TSL2772_REVID 0x11 +#define TSL2772_CHIPID 0x12 +#define TSL2772_STATUS 0x13 +#define TSL2772_ALS_CHAN0LO 0x14 +#define TSL2772_ALS_CHAN0HI 0x15 +#define TSL2772_ALS_CHAN1LO 0x16 +#define TSL2772_ALS_CHAN1HI 0x17 +#define TSL2772_PRX_LO 0x18 +#define TSL2772_PRX_HI 0x19 + +/* tsl2772 cmd reg masks */ +#define TSL2772_CMD_REG 0x80 +#define TSL2772_CMD_SPL_FN 0x60 +#define TSL2772_CMD_REPEAT_PROTO 0x00 +#define TSL2772_CMD_AUTOINC_PROTO 0x20 + +#define TSL2772_CMD_PROX_INT_CLR 0X05 +#define TSL2772_CMD_ALS_INT_CLR 0x06 +#define TSL2772_CMD_PROXALS_INT_CLR 0X07 + +/* tsl2772 cntrl reg masks */ +#define TSL2772_CNTL_ADC_ENBL 0x02 +#define TSL2772_CNTL_PWR_ON 0x01 + +/* tsl2772 status reg masks */ +#define TSL2772_STA_ADC_VALID 0x01 +#define TSL2772_STA_PRX_VALID 0x02 +#define TSL2772_STA_ADC_PRX_VALID (TSL2772_STA_ADC_VALID | \ + TSL2772_STA_PRX_VALID) +#define TSL2772_STA_ALS_INTR 0x10 +#define TSL2772_STA_PRX_INTR 0x20 + +/* tsl2772 cntrl reg masks */ +#define TSL2772_CNTL_REG_CLEAR 0x00 +#define TSL2772_CNTL_PROX_INT_ENBL 0X20 +#define TSL2772_CNTL_ALS_INT_ENBL 0X10 +#define TSL2772_CNTL_WAIT_TMR_ENBL 0X08 +#define TSL2772_CNTL_PROX_DET_ENBL 0X04 +#define TSL2772_CNTL_PWRON 0x01 +#define TSL2772_CNTL_ALSPON_ENBL 0x03 +#define TSL2772_CNTL_INTALSPON_ENBL 0x13 +#define TSL2772_CNTL_PROXPON_ENBL 0x0F +#define TSL2772_CNTL_INTPROXPON_ENBL 0x2F + +#define TSL2772_ALS_GAIN_TRIM_MIN 250 +#define TSL2772_ALS_GAIN_TRIM_MAX 4000 + +/* Device family members */ +enum { + tsl2571, + tsl2671, + tmd2671, + tsl2771, + tmd2771, + tsl2572, + tsl2672, + tmd2672, + tsl2772, + tmd2772 +}; + +enum { + TSL2772_CHIP_UNKNOWN = 0, + TSL2772_CHIP_WORKING = 1, + TSL2772_CHIP_SUSPENDED = 2 +}; + +/* Per-device data */ +struct tsl2772_als_info { + u16 als_ch0; + u16 als_ch1; + u16 lux; +}; + +struct tsl2772_chip_info { + int chan_table_elements; + struct iio_chan_spec channel_with_events[4]; + struct iio_chan_spec channel_without_events[4]; + const struct iio_info *info; +}; + +struct tsl2772_chip { + kernel_ulong_t id; + struct mutex prox_mutex; + struct mutex als_mutex; + struct i2c_client *client; + u16 prox_data; + struct tsl2772_als_info als_cur_info; + struct tsl2772_settings settings; + struct tsl2772_platform_data *pdata; + int als_gain_time_scale; + int als_saturation; + int tsl2772_chip_status; + u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; + const struct tsl2772_chip_info *chip_info; + const struct iio_info *info; + s64 event_timestamp; + /* + * This structure is intentionally large to accommodate + * updates via sysfs. + * Sized to 9 = max 8 segments + 1 termination segment + */ + struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE]; +}; + +/* + * Different devices require different coefficents, and these numbers were + * derived from the 'Lux Equation' section of the various device datasheets. + * All of these coefficients assume a Glass Attenuation (GA) factor of 1. + * The coefficients are multiplied by 1000 to avoid floating point operations. + * The two rows in each table correspond to the Lux1 and Lux2 equations from + * the datasheets. + */ +static const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 53000, 106000 }, + { 31800, 53000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 24000, 48000 }, + { 14400, 24000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 60000, 112200 }, + { 37800, 60000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { + { 20000, 35000 }, + { 12600, 20000 }, + { 0, 0 }, +}; + +static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { + [tsl2571] = tsl2x71_lux_table, + [tsl2671] = tsl2x71_lux_table, + [tmd2671] = tmd2x71_lux_table, + [tsl2771] = tsl2x71_lux_table, + [tmd2771] = tmd2x71_lux_table, + [tsl2572] = tsl2x72_lux_table, + [tsl2672] = tsl2x72_lux_table, + [tmd2672] = tmd2x72_lux_table, + [tsl2772] = tsl2x72_lux_table, + [tmd2772] = tmd2x72_lux_table, +}; + +static const struct tsl2772_settings tsl2772_default_settings = { + .als_time = 255, /* 2.72 / 2.73 ms */ + .als_gain = 0, + .prox_time = 255, /* 2.72 / 2.73 ms */ + .prox_gain = 0, + .wait_time = 255, + .als_prox_config = 0, + .als_gain_trim = 1000, + .als_cal_target = 150, + .als_persistence = 1, + .als_interrupt_en = false, + .als_thresh_low = 200, + .als_thresh_high = 256, + .prox_persistence = 1, + .prox_interrupt_en = false, + .prox_thres_low = 0, + .prox_thres_high = 512, + .prox_max_samples_cal = 30, + .prox_pulse_count = 8, + .prox_diode = TSL2772_DIODE1, + .prox_power = TSL2772_100_mA +}; + +static const s16 tsl2772_als_gain[] = { + 1, + 8, + 16, + 120 +}; + +static const s16 tsl2772_prox_gain[] = { + 1, + 2, + 4, + 8 +}; + +static const int tsl2772_int_time_avail[][6] = { + [tsl2571] = { 0, 2720, 0, 2720, 0, 696000 }, + [tsl2671] = { 0, 2720, 0, 2720, 0, 696000 }, + [tmd2671] = { 0, 2720, 0, 2720, 0, 696000 }, + [tsl2771] = { 0, 2720, 0, 2720, 0, 696000 }, + [tmd2771] = { 0, 2720, 0, 2720, 0, 696000 }, + [tsl2572] = { 0, 2730, 0, 2730, 0, 699000 }, + [tsl2672] = { 0, 2730, 0, 2730, 0, 699000 }, + [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, + [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, + [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, +}; + +static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; + +static int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 }; + +/* Channel variations */ +enum { + ALS, + PRX, + ALSPRX, + PRX2, + ALSPRX2, +}; + +static const u8 device_channel_config[] = { + [tsl2571] = ALS, + [tsl2671] = PRX, + [tmd2671] = PRX, + [tsl2771] = ALSPRX, + [tmd2771] = ALSPRX, + [tsl2572] = ALS, + [tsl2672] = PRX2, + [tmd2672] = PRX2, + [tsl2772] = ALSPRX2, + [tmd2772] = ALSPRX2 +}; + +static int tsl2772_read_status(struct tsl2772_chip *chip) +{ + int ret; + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_STATUS); + if (ret < 0) + dev_err(&chip->client->dev, + "%s: failed to read STATUS register: %d\n", __func__, + ret); + + return ret; +} + +static int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data) +{ + int ret; + + ret = i2c_smbus_write_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_CNTRL, data); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to write to control register %x: %d\n", + __func__, data, ret); + } + + return ret; +} + +static int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, + int upper_reg) +{ + u8 buf[2]; + int ret; + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO | + lower_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to enable auto increment protocol: %d\n", + __func__, ret); + return ret; + } + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | lower_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to read from register %x: %d\n", __func__, + lower_reg, ret); + return ret; + } + buf[0] = ret; + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | upper_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to read from register %x: %d\n", __func__, + upper_reg, ret); + return ret; + } + buf[1] = ret; + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO | + lower_reg); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to enable repeated byte protocol: %d\n", + __func__, ret); + return ret; + } + + return le16_to_cpup((const __le16 *)&buf[0]); +} + +/** + * tsl2772_get_lux() - Reads and calculates current lux value. + * @indio_dev: pointer to IIO device + * + * The raw ch0 and ch1 values of the ambient light sensed in the last + * integration cycle are read from the device. The raw values are multiplied + * by a device-specific scale factor, and divided by the integration time and + * device gain. The code supports multiple lux equations through the lux table + * coefficients. A lux gain trim is applied to each lux equation, and then the + * maximum lux within the interval 0..65535 is selected. + */ +static int tsl2772_get_lux(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + struct tsl2772_lux *p; + int max_lux, ret; + bool overflow; + + mutex_lock(&chip->als_mutex); + + if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) { + dev_err(&chip->client->dev, "%s: device is not enabled\n", + __func__); + ret = -EBUSY; + goto out_unlock; + } + + ret = tsl2772_read_status(chip); + if (ret < 0) + goto out_unlock; + + if (!(ret & TSL2772_STA_ADC_VALID)) { + dev_err(&chip->client->dev, + "%s: data not valid yet\n", __func__); + ret = chip->als_cur_info.lux; /* return LAST VALUE */ + goto out_unlock; + } + + ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO, + TSL2772_ALS_CHAN0HI); + if (ret < 0) + goto out_unlock; + chip->als_cur_info.als_ch0 = ret; + + ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO, + TSL2772_ALS_CHAN1HI); + if (ret < 0) + goto out_unlock; + chip->als_cur_info.als_ch1 = ret; + + if (chip->als_cur_info.als_ch0 >= chip->als_saturation) { + max_lux = TSL2772_LUX_CALC_OVER_FLOW; + goto update_struct_with_max_lux; + } + + if (!chip->als_cur_info.als_ch0) { + /* have no data, so return LAST VALUE */ + ret = chip->als_cur_info.lux; + goto out_unlock; + } + + max_lux = 0; + overflow = false; + for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0; + p++) { + int lux; + + lux = ((chip->als_cur_info.als_ch0 * p->ch0) - + (chip->als_cur_info.als_ch1 * p->ch1)) / + chip->als_gain_time_scale; + + /* + * The als_gain_trim can have a value within the range 250..4000 + * and is a multiplier for the lux. A trim of 1000 makes no + * changes to the lux, less than 1000 scales it down, and + * greater than 1000 scales it up. + */ + lux = (lux * chip->settings.als_gain_trim) / 1000; + + if (lux > TSL2772_LUX_CALC_OVER_FLOW) { + overflow = true; + continue; + } + + max_lux = max(max_lux, lux); + } + + if (overflow && max_lux == 0) + max_lux = TSL2772_LUX_CALC_OVER_FLOW; + +update_struct_with_max_lux: + chip->als_cur_info.lux = max_lux; + ret = max_lux; + +out_unlock: + mutex_unlock(&chip->als_mutex); + + return ret; +} + +/** + * tsl2772_get_prox() - Reads proximity data registers and updates + * chip->prox_data. + * + * @indio_dev: pointer to IIO device + */ +static int tsl2772_get_prox(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret; + + mutex_lock(&chip->prox_mutex); + + ret = tsl2772_read_status(chip); + if (ret < 0) + goto prox_poll_err; + + switch (chip->id) { + case tsl2571: + case tsl2671: + case tmd2671: + case tsl2771: + case tmd2771: + if (!(ret & TSL2772_STA_ADC_VALID)) { + ret = -EINVAL; + goto prox_poll_err; + } + break; + case tsl2572: + case tsl2672: + case tmd2672: + case tsl2772: + case tmd2772: + if (!(ret & TSL2772_STA_PRX_VALID)) { + ret = -EINVAL; + goto prox_poll_err; + } + break; + } + + ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); + if (ret < 0) + goto prox_poll_err; + chip->prox_data = ret; + +prox_poll_err: + mutex_unlock(&chip->prox_mutex); + + return ret; +} + +/** + * tsl2772_defaults() - Populates the device nominal operating parameters + * with those provided by a 'platform' data struct or + * with prefined defaults. + * + * @chip: pointer to device structure. + */ +static void tsl2772_defaults(struct tsl2772_chip *chip) +{ + /* If Operational settings defined elsewhere.. */ + if (chip->pdata && chip->pdata->platform_default_settings) + memcpy(&chip->settings, chip->pdata->platform_default_settings, + sizeof(tsl2772_default_settings)); + else + memcpy(&chip->settings, &tsl2772_default_settings, + sizeof(tsl2772_default_settings)); + + /* Load up the proper lux table. */ + if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0) + memcpy(chip->tsl2772_device_lux, + chip->pdata->platform_lux_table, + sizeof(chip->pdata->platform_lux_table)); + else + memcpy(chip->tsl2772_device_lux, + tsl2772_default_lux_table_group[chip->id], + TSL2772_DEFAULT_TABLE_BYTES); +} + +/** + * tsl2772_als_calibrate() - Obtain single reading and calculate + * the als_gain_trim. + * + * @indio_dev: pointer to IIO device + */ +static int tsl2772_als_calibrate(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret, lux_val; + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_CNTRL); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to read from the CNTRL register\n", + __func__); + return ret; + } + + if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) + != (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) { + dev_err(&chip->client->dev, + "%s: Device is not powered on and/or ADC is not enabled\n", + __func__); + return -EINVAL; + } else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) { + dev_err(&chip->client->dev, + "%s: The two ADC channels have not completed an integration cycle\n", + __func__); + return -ENODATA; + } + + lux_val = tsl2772_get_lux(indio_dev); + if (lux_val < 0) { + dev_err(&chip->client->dev, + "%s: failed to get lux\n", __func__); + return lux_val; + } + + ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / + lux_val; + if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) + return -ERANGE; + + chip->settings.als_gain_trim = ret; + + return ret; +} + +static int tsl2772_chip_on(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret, i, als_count, als_time_us; + u8 *dev_reg, reg_val; + + /* Non calculated parameters */ + chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time; + chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time; + chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time; + chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] = + chip->settings.als_prox_config; + + chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] = + (chip->settings.als_thresh_low) & 0xFF; + chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] = + (chip->settings.als_thresh_low >> 8) & 0xFF; + chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] = + (chip->settings.als_thresh_high) & 0xFF; + chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] = + (chip->settings.als_thresh_high >> 8) & 0xFF; + chip->tsl2772_config[TSL2772_PERSISTENCE] = + (chip->settings.prox_persistence & 0xFF) << 4 | + (chip->settings.als_persistence & 0xFF); + + chip->tsl2772_config[TSL2772_PRX_COUNT] = + chip->settings.prox_pulse_count; + chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] = + (chip->settings.prox_thres_low) & 0xFF; + chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] = + (chip->settings.prox_thres_low >> 8) & 0xFF; + chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] = + (chip->settings.prox_thres_high) & 0xFF; + chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] = + (chip->settings.prox_thres_high >> 8) & 0xFF; + + /* and make sure we're not already on */ + if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { + /* if forcing a register update - turn off, then on */ + dev_info(&chip->client->dev, "device is already enabled\n"); + return -EINVAL; + } + + /* Set the gain based on tsl2772_settings struct */ + chip->tsl2772_config[TSL2772_GAIN] = + (chip->settings.als_gain & 0xFF) | + ((chip->settings.prox_gain & 0xFF) << 2) | + (chip->settings.prox_diode << 4) | + (chip->settings.prox_power << 6); + + /* set chip time scaling and saturation */ + als_count = 256 - chip->settings.als_time; + als_time_us = als_count * tsl2772_int_time_avail[chip->id][3]; + chip->als_saturation = als_count * 768; /* 75% of full scale */ + chip->als_gain_time_scale = als_time_us * + tsl2772_als_gain[chip->settings.als_gain]; + + /* + * TSL2772 Specific power-on / adc enable sequence + * Power on the device 1st. + */ + ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); + if (ret < 0) + return ret; + + /* + * Use the following shadow copy for our delay before enabling ADC. + * Write all the registers. + */ + for (i = 0, dev_reg = chip->tsl2772_config; + i < TSL2772_MAX_CONFIG_REG; i++) { + int reg = TSL2772_CMD_REG + i; + + ret = i2c_smbus_write_byte_data(chip->client, reg, + *dev_reg++); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to write to register %x: %d\n", + __func__, reg, ret); + return ret; + } + } + + /* Power-on settling time */ + usleep_range(3000, 3500); + + reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL | + TSL2772_CNTL_PROX_DET_ENBL; + if (chip->settings.als_interrupt_en) + reg_val |= TSL2772_CNTL_ALS_INT_ENBL; + if (chip->settings.prox_interrupt_en) + reg_val |= TSL2772_CNTL_PROX_INT_ENBL; + + ret = tsl2772_write_control_reg(chip, reg_val); + if (ret < 0) + return ret; + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | + TSL2772_CMD_PROXALS_INT_CLR); + if (ret < 0) { + dev_err(&chip->client->dev, + "%s: failed to clear interrupt status: %d\n", + __func__, ret); + return ret; + } + + chip->tsl2772_chip_status = TSL2772_CHIP_WORKING; + + return ret; +} + +static int tsl2772_chip_off(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + /* turn device off */ + chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED; + return tsl2772_write_control_reg(chip, 0x00); +} + +/** + * tsl2772_invoke_change - power cycle the device to implement the user + * parameters + * @indio_dev: pointer to IIO device + * + * Obtain and lock both ALS and PROX resources, determine and save device state + * (On/Off), cycle device to implement updated parameter, put device back into + * proper state, and unlock resource. + */ +static int tsl2772_invoke_change(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int device_status = chip->tsl2772_chip_status; + int ret; + + mutex_lock(&chip->als_mutex); + mutex_lock(&chip->prox_mutex); + + if (device_status == TSL2772_CHIP_WORKING) { + ret = tsl2772_chip_off(indio_dev); + if (ret < 0) + goto unlock; + } + + ret = tsl2772_chip_on(indio_dev); + +unlock: + mutex_unlock(&chip->prox_mutex); + mutex_unlock(&chip->als_mutex); + + return ret; +} + +static int tsl2772_prox_cal(struct iio_dev *indio_dev) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int prox_history[MAX_SAMPLES_CAL + 1]; + int i, ret, mean, max, sample_sum; + + if (chip->settings.prox_max_samples_cal < 1 || + chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) + return -EINVAL; + + for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { + usleep_range(15000, 17500); + ret = tsl2772_get_prox(indio_dev); + if (ret < 0) + return ret; + + prox_history[i] = chip->prox_data; + } + + sample_sum = 0; + max = INT_MIN; + for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { + sample_sum += prox_history[i]; + max = max(max, prox_history[i]); + } + mean = sample_sum / chip->settings.prox_max_samples_cal; + + chip->settings.prox_thres_high = (max << 1) - mean; + + return tsl2772_invoke_change(indio_dev); +} + +static int tsl2772_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type == IIO_INTENSITY) { + *length = ARRAY_SIZE(tsl2772_int_calibscale_avail); + *vals = tsl2772_int_calibscale_avail; + } else { + *length = ARRAY_SIZE(tsl2772_prox_calibscale_avail); + *vals = tsl2772_prox_calibscale_avail; + } + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_INT_TIME: + *length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]); + *vals = tsl2772_int_time_avail[chip->id]; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_RANGE; + } + + return -EINVAL; +} + +static ssize_t in_illuminance0_target_input_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); + + return snprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); +} + +static ssize_t in_illuminance0_target_input_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + u16 value; + int ret; + + if (kstrtou16(buf, 0, &value)) + return -EINVAL; + + chip->settings.als_cal_target = value; + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t in_illuminance0_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool value; + int ret; + + if (kstrtobool(buf, &value) || !value) + return -EINVAL; + + ret = tsl2772_als_calibrate(indio_dev); + if (ret < 0) + return ret; + + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t in_illuminance0_lux_table_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); + int i = 0; + int offset = 0; + + while (i < TSL2772_MAX_LUX_TABLE_SIZE) { + offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,", + chip->tsl2772_device_lux[i].ch0, + chip->tsl2772_device_lux[i].ch1); + if (chip->tsl2772_device_lux[i].ch0 == 0) { + /* + * We just printed the first "0" entry. + * Now get rid of the extra "," and break. + */ + offset--; + break; + } + i++; + } + + offset += snprintf(buf + offset, PAGE_SIZE, "\n"); + return offset; +} + +static ssize_t in_illuminance0_lux_table_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl2772_chip *chip = iio_priv(indio_dev); + int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1]; + int n, ret; + + get_options(buf, ARRAY_SIZE(value), value); + + /* + * We now have an array of ints starting at value[1], and + * enumerated by value[0]. + * We expect each group of two ints to be one table entry, + * and the last table entry is all 0. + */ + n = value[0]; + if ((n % 2) || n < 4 || + n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) + return -EINVAL; + + if ((value[(n - 1)] | value[n]) != 0) + return -EINVAL; + + if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { + ret = tsl2772_chip_off(indio_dev); + if (ret < 0) + return ret; + } + + /* Zero out the table */ + memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux)); + memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4)); + + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static ssize_t in_proximity0_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + bool value; + int ret; + + if (kstrtobool(buf, &value) || !value) + return -EINVAL; + + ret = tsl2772_prox_cal(indio_dev); + if (ret < 0) + return ret; + + ret = tsl2772_invoke_change(indio_dev); + if (ret < 0) + return ret; + + return len; +} + +static int tsl2772_read_interrupt_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + if (chan->type == IIO_INTENSITY) + return chip->settings.als_interrupt_en; + else + return chip->settings.prox_interrupt_en; +} + +static int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + int val) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + if (chan->type == IIO_INTENSITY) + chip->settings.als_interrupt_en = val ? true : false; + else + chip->settings.prox_interrupt_en = val ? true : false; + + return tsl2772_invoke_change(indio_dev); +} + +static int tsl2772_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int ret = -EINVAL, count, persistence; + u8 time; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (chan->type == IIO_INTENSITY) { + switch (dir) { + case IIO_EV_DIR_RISING: + chip->settings.als_thresh_high = val; + ret = 0; + break; + case IIO_EV_DIR_FALLING: + chip->settings.als_thresh_low = val; + ret = 0; + break; + default: + break; + } + } else { + switch (dir) { + case IIO_EV_DIR_RISING: + chip->settings.prox_thres_high = val; + ret = 0; + break; + case IIO_EV_DIR_FALLING: + chip->settings.prox_thres_low = val; + ret = 0; + break; + default: + break; + } + } + break; + case IIO_EV_INFO_PERIOD: + if (chan->type == IIO_INTENSITY) + time = chip->settings.als_time; + else + time = chip->settings.prox_time; + + count = 256 - time; + persistence = ((val * 1000000) + val2) / + (count * tsl2772_int_time_avail[chip->id][3]); + + if (chan->type == IIO_INTENSITY) { + /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ + if (persistence > 3) + persistence = (persistence / 5) + 3; + + chip->settings.als_persistence = persistence; + } else { + chip->settings.prox_persistence = persistence; + } + + ret = 0; + break; + default: + break; + } + + if (ret < 0) + return ret; + + return tsl2772_invoke_change(indio_dev); +} + +static int tsl2772_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + int filter_delay, persistence; + u8 time; + + switch (info) { + case IIO_EV_INFO_VALUE: + if (chan->type == IIO_INTENSITY) { + switch (dir) { + case IIO_EV_DIR_RISING: + *val = chip->settings.als_thresh_high; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = chip->settings.als_thresh_low; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } else { + switch (dir) { + case IIO_EV_DIR_RISING: + *val = chip->settings.prox_thres_high; + return IIO_VAL_INT; + case IIO_EV_DIR_FALLING: + *val = chip->settings.prox_thres_low; + return IIO_VAL_INT; + default: + return -EINVAL; + } + } + break; + case IIO_EV_INFO_PERIOD: + if (chan->type == IIO_INTENSITY) { + time = chip->settings.als_time; + persistence = chip->settings.als_persistence; + + /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ + if (persistence > 3) + persistence = (persistence - 3) * 5; + } else { + time = chip->settings.prox_time; + persistence = chip->settings.prox_persistence; + } + + filter_delay = persistence * (256 - time) * + tsl2772_int_time_avail[chip->id][3]; + + *val = filter_delay / 1000000; + *val2 = filter_delay % 1000000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int tsl2772_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (chan->type) { + case IIO_LIGHT: + tsl2772_get_lux(indio_dev); + *val = chip->als_cur_info.lux; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_INTENSITY: + tsl2772_get_lux(indio_dev); + if (chan->channel == 0) + *val = chip->als_cur_info.als_ch0; + else + *val = chip->als_cur_info.als_ch1; + return IIO_VAL_INT; + case IIO_PROXIMITY: + tsl2772_get_prox(indio_dev); + *val = chip->prox_data; + return IIO_VAL_INT; + default: + return -EINVAL; + } + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type == IIO_LIGHT) + *val = tsl2772_als_gain[chip->settings.als_gain]; + else + *val = tsl2772_prox_gain[chip->settings.prox_gain]; + return IIO_VAL_INT; + case IIO_CHAN_INFO_CALIBBIAS: + *val = chip->settings.als_gain_trim; + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = (256 - chip->settings.als_time) * + tsl2772_int_time_avail[chip->id][3]; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int tsl2772_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, + int val2, + long mask) +{ + struct tsl2772_chip *chip = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type == IIO_INTENSITY) { + switch (val) { + case 1: + chip->settings.als_gain = 0; + break; + case 8: + chip->settings.als_gain = 1; + break; + case 16: + chip->settings.als_gain = 2; + break; + case 120: + chip->settings.als_gain = 3; + break; + default: + return -EINVAL; + } + } else { + switch (val) { + case 1: + chip->settings.prox_gain = 0; + break; + case 2: + chip->settings.prox_gain = 1; + break; + case 4: + chip->settings.prox_gain = 2; + break; + case 8: + chip->settings.prox_gain = 3; + break; + default: + return -EINVAL; + } + } + break; + case IIO_CHAN_INFO_CALIBBIAS: + if (val < TSL2772_ALS_GAIN_TRIM_MIN || + val > TSL2772_ALS_GAIN_TRIM_MAX) + return -EINVAL; + + chip->settings.als_gain_trim = val; + break; + case IIO_CHAN_INFO_INT_TIME: + if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] || + val2 > tsl2772_int_time_avail[chip->id][5]) + return -EINVAL; + + chip->settings.als_time = 256 - + (val2 / tsl2772_int_time_avail[chip->id][3]); + break; + default: + return -EINVAL; + } + + return tsl2772_invoke_change(indio_dev); +} + +static DEVICE_ATTR_RW(in_illuminance0_target_input); + +static DEVICE_ATTR_WO(in_illuminance0_calibrate); + +static DEVICE_ATTR_WO(in_proximity0_calibrate); + +static DEVICE_ATTR_RW(in_illuminance0_lux_table); + +/* Use the default register values to identify the Taos device */ +static int tsl2772_device_id_verif(int id, int target) +{ + switch (target) { + case tsl2571: + case tsl2671: + case tsl2771: + return (id & 0xf0) == TRITON_ID; + case tmd2671: + case tmd2771: + return (id & 0xf0) == HALIBUT_ID; + case tsl2572: + case tsl2672: + case tmd2672: + case tsl2772: + case tmd2772: + return (id & 0xf0) == SWORDFISH_ID; + } + + return -EINVAL; +} + +static irqreturn_t tsl2772_event_handler(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct tsl2772_chip *chip = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + int ret; + + ret = tsl2772_read_status(chip); + if (ret < 0) + return IRQ_HANDLED; + + /* What type of interrupt do we need to process */ + if (ret & TSL2772_STA_PRX_INTR) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + } + + if (ret & TSL2772_STA_ALS_INTR) { + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, + 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + timestamp); + } + + ret = i2c_smbus_write_byte(chip->client, + TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | + TSL2772_CMD_PROXALS_INT_CLR); + if (ret < 0) + dev_err(&chip->client->dev, + "%s: failed to clear interrupt status: %d\n", + __func__, ret); + + return IRQ_HANDLED; +} + +static struct attribute *tsl2772_ALS_device_attrs[] = { + &dev_attr_in_illuminance0_target_input.attr, + &dev_attr_in_illuminance0_calibrate.attr, + &dev_attr_in_illuminance0_lux_table.attr, + NULL +}; + +static struct attribute *tsl2772_PRX_device_attrs[] = { + &dev_attr_in_proximity0_calibrate.attr, + NULL +}; + +static struct attribute *tsl2772_ALSPRX_device_attrs[] = { + &dev_attr_in_illuminance0_target_input.attr, + &dev_attr_in_illuminance0_calibrate.attr, + &dev_attr_in_illuminance0_lux_table.attr, + NULL +}; + +static struct attribute *tsl2772_PRX2_device_attrs[] = { + &dev_attr_in_proximity0_calibrate.attr, + NULL +}; + +static struct attribute *tsl2772_ALSPRX2_device_attrs[] = { + &dev_attr_in_illuminance0_target_input.attr, + &dev_attr_in_illuminance0_calibrate.attr, + &dev_attr_in_illuminance0_lux_table.attr, + &dev_attr_in_proximity0_calibrate.attr, + NULL +}; + +static const struct attribute_group tsl2772_device_attr_group_tbl[] = { + [ALS] = { + .attrs = tsl2772_ALS_device_attrs, + }, + [PRX] = { + .attrs = tsl2772_PRX_device_attrs, + }, + [ALSPRX] = { + .attrs = tsl2772_ALSPRX_device_attrs, + }, + [PRX2] = { + .attrs = tsl2772_PRX2_device_attrs, + }, + [ALSPRX2] = { + .attrs = tsl2772_ALSPRX2_device_attrs, + }, +}; + +#define TSL2772_DEVICE_INFO(type)[type] = \ + { \ + .attrs = &tsl2772_device_attr_group_tbl[type], \ + .read_raw = &tsl2772_read_raw, \ + .read_avail = &tsl2772_read_avail, \ + .write_raw = &tsl2772_write_raw, \ + .read_event_value = &tsl2772_read_event_value, \ + .write_event_value = &tsl2772_write_event_value, \ + .read_event_config = &tsl2772_read_interrupt_config, \ + .write_event_config = &tsl2772_write_interrupt_config, \ + } + +static const struct iio_info tsl2772_device_info[] = { + TSL2772_DEVICE_INFO(ALS), + TSL2772_DEVICE_INFO(PRX), + TSL2772_DEVICE_INFO(ALSPRX), + TSL2772_DEVICE_INFO(PRX2), + TSL2772_DEVICE_INFO(ALSPRX2), +}; + +static const struct iio_event_spec tsl2772_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = { + [ALS] = { + .channel_with_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + }, + }, + .channel_without_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + }, + }, + .chan_table_elements = 3, + .info = &tsl2772_device_info[ALS], + }, + [PRX] = { + .channel_with_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, + }, + .chan_table_elements = 1, + .info = &tsl2772_device_info[PRX], + }, + [ALSPRX] = { + .channel_with_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, + }, + .chan_table_elements = 4, + .info = &tsl2772_device_info[ALSPRX], + }, + [PRX2] = { + .channel_with_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, + }, + .chan_table_elements = 1, + .info = &tsl2772_device_info[PRX2], + }, + [ALSPRX2] = { + .channel_with_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + .event_spec = tsl2772_events, + .num_event_specs = ARRAY_SIZE(tsl2772_events), + }, + }, + .channel_without_events = { + { + .type = IIO_LIGHT, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) | + BIT(IIO_CHAN_INFO_CALIBBIAS), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, { + .type = IIO_INTENSITY, + .indexed = 1, + .channel = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + }, { + .type = IIO_PROXIMITY, + .indexed = 1, + .channel = 0, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_separate_available = + BIT(IIO_CHAN_INFO_CALIBSCALE), + }, + }, + .chan_table_elements = 4, + .info = &tsl2772_device_info[ALSPRX2], + }, +}; + +static int tsl2772_probe(struct i2c_client *clientp, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct tsl2772_chip *chip; + int ret; + + indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + chip->client = clientp; + i2c_set_clientdata(clientp, indio_dev); + + ret = i2c_smbus_read_byte_data(chip->client, + TSL2772_CMD_REG | TSL2772_CHIPID); + if (ret < 0) + return ret; + + if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) { + dev_info(&chip->client->dev, + "%s: i2c device found does not match expected id\n", + __func__); + return -EINVAL; + } + + ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); + if (ret < 0) { + dev_err(&clientp->dev, + "%s: Failed to write to CMD register: %d\n", + __func__, ret); + return ret; + } + + mutex_init(&chip->als_mutex); + mutex_init(&chip->prox_mutex); + + chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN; + chip->pdata = dev_get_platdata(&clientp->dev); + chip->id = id->driver_data; + chip->chip_info = + &tsl2772_chip_info_tbl[device_channel_config[id->driver_data]]; + + indio_dev->info = chip->chip_info->info; + indio_dev->dev.parent = &clientp->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = chip->client->name; + indio_dev->num_channels = chip->chip_info->chan_table_elements; + + if (clientp->irq) { + indio_dev->channels = chip->chip_info->channel_with_events; + + ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, + NULL, + &tsl2772_event_handler, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "TSL2772_event", + indio_dev); + if (ret) { + dev_err(&clientp->dev, + "%s: irq request failed\n", __func__); + return ret; + } + } else { + indio_dev->channels = chip->chip_info->channel_without_events; + } + + tsl2772_defaults(chip); + ret = tsl2772_chip_on(indio_dev); + if (ret < 0) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) { + tsl2772_chip_off(indio_dev); + dev_err(&clientp->dev, + "%s: iio registration failed\n", __func__); + return ret; + } + + return 0; +} + +static int tsl2772_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return tsl2772_chip_off(indio_dev); +} + +static int tsl2772_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + return tsl2772_chip_on(indio_dev); +} + +static int tsl2772_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + tsl2772_chip_off(indio_dev); + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct i2c_device_id tsl2772_idtable[] = { + { "tsl2571", tsl2571 }, + { "tsl2671", tsl2671 }, + { "tmd2671", tmd2671 }, + { "tsl2771", tsl2771 }, + { "tmd2771", tmd2771 }, + { "tsl2572", tsl2572 }, + { "tsl2672", tsl2672 }, + { "tmd2672", tmd2672 }, + { "tsl2772", tsl2772 }, + { "tmd2772", tmd2772 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, tsl2772_idtable); + +static const struct of_device_id tsl2772_of_match[] = { + { .compatible = "amstaos,tsl2571" }, + { .compatible = "amstaos,tsl2671" }, + { .compatible = "amstaos,tmd2671" }, + { .compatible = "amstaos,tsl2771" }, + { .compatible = "amstaos,tmd2771" }, + { .compatible = "amstaos,tsl2572" }, + { .compatible = "amstaos,tsl2672" }, + { .compatible = "amstaos,tmd2672" }, + { .compatible = "amstaos,tsl2772" }, + { .compatible = "amstaos,tmd2772" }, + {} +}; +MODULE_DEVICE_TABLE(of, tsl2772_of_match); + +static const struct dev_pm_ops tsl2772_pm_ops = { + .suspend = tsl2772_suspend, + .resume = tsl2772_resume, +}; + +static struct i2c_driver tsl2772_driver = { + .driver = { + .name = "tsl2772", + .of_match_table = tsl2772_of_match, + .pm = &tsl2772_pm_ops, + }, + .id_table = tsl2772_idtable, + .probe = tsl2772_probe, + .remove = tsl2772_remove, +}; + +module_i2c_driver(tsl2772_driver); + +MODULE_AUTHOR("J. August Brenner "); +MODULE_AUTHOR("Brian Masney "); +MODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/Kconfig b/drivers/staging/iio/Kconfig index bd9445956511..aee2335a25a1 100644 --- a/drivers/staging/iio/Kconfig +++ b/drivers/staging/iio/Kconfig @@ -11,7 +11,6 @@ source "drivers/staging/iio/cdc/Kconfig" source "drivers/staging/iio/frequency/Kconfig" source "drivers/staging/iio/gyro/Kconfig" source "drivers/staging/iio/impedance-analyzer/Kconfig" -source "drivers/staging/iio/light/Kconfig" source "drivers/staging/iio/meter/Kconfig" source "drivers/staging/iio/resolver/Kconfig" diff --git a/drivers/staging/iio/Makefile b/drivers/staging/iio/Makefile index e99a375c07b9..c28d657497de 100644 --- a/drivers/staging/iio/Makefile +++ b/drivers/staging/iio/Makefile @@ -10,6 +10,5 @@ obj-y += cdc/ obj-y += frequency/ obj-y += gyro/ obj-y += impedance-analyzer/ -obj-y += light/ obj-y += meter/ obj-y += resolver/ diff --git a/drivers/staging/iio/light/Kconfig b/drivers/staging/iio/light/Kconfig deleted file mode 100644 index dfa37386ad2c..000000000000 --- a/drivers/staging/iio/light/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -# -# Light sensors -# -menu "Light sensors" - -config TSL2772 - tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" - depends on I2C - help - Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672, - tmd2672, tsl2772, tmd2772 devices. - Provides iio_events and direct access via sysfs. - -endmenu diff --git a/drivers/staging/iio/light/Makefile b/drivers/staging/iio/light/Makefile deleted file mode 100644 index e7e77a11f02a..000000000000 --- a/drivers/staging/iio/light/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -# -# Makefile for industrial I/O Light sensors -# - -obj-$(CONFIG_TSL2772) += tsl2772.o diff --git a/drivers/staging/iio/light/tsl2772.c b/drivers/staging/iio/light/tsl2772.c deleted file mode 100644 index a59bf39c28d4..000000000000 --- a/drivers/staging/iio/light/tsl2772.c +++ /dev/null @@ -1,1800 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Device driver for monitoring ambient light intensity in (lux) and proximity - * detection (prox) within the TAOS TSL2571, TSL2671, TMD2671, TSL2771, TMD2771, - * TSL2572, TSL2672, TMD2672, TSL2772, and TMD2772 devices. - * - * Copyright (c) 2012, TAOS Corporation. - * Copyright (c) 2017-2018 Brian Masney - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "tsl2772.h" - -/* Cal defs */ -#define PROX_STAT_CAL 0 -#define PROX_STAT_SAMP 1 -#define MAX_SAMPLES_CAL 200 - -/* TSL2772 Device ID */ -#define TRITON_ID 0x00 -#define SWORDFISH_ID 0x30 -#define HALIBUT_ID 0x20 - -/* Lux calculation constants */ -#define TSL2772_LUX_CALC_OVER_FLOW 65535 - -/* - * TAOS Register definitions - Note: depending on device, some of these register - * are not used and the register address is benign. - */ - -/* Register offsets */ -#define TSL2772_MAX_CONFIG_REG 16 - -/* Device Registers and Masks */ -#define TSL2772_CNTRL 0x00 -#define TSL2772_ALS_TIME 0X01 -#define TSL2772_PRX_TIME 0x02 -#define TSL2772_WAIT_TIME 0x03 -#define TSL2772_ALS_MINTHRESHLO 0X04 -#define TSL2772_ALS_MINTHRESHHI 0X05 -#define TSL2772_ALS_MAXTHRESHLO 0X06 -#define TSL2772_ALS_MAXTHRESHHI 0X07 -#define TSL2772_PRX_MINTHRESHLO 0X08 -#define TSL2772_PRX_MINTHRESHHI 0X09 -#define TSL2772_PRX_MAXTHRESHLO 0X0A -#define TSL2772_PRX_MAXTHRESHHI 0X0B -#define TSL2772_PERSISTENCE 0x0C -#define TSL2772_ALS_PRX_CONFIG 0x0D -#define TSL2772_PRX_COUNT 0x0E -#define TSL2772_GAIN 0x0F -#define TSL2772_NOTUSED 0x10 -#define TSL2772_REVID 0x11 -#define TSL2772_CHIPID 0x12 -#define TSL2772_STATUS 0x13 -#define TSL2772_ALS_CHAN0LO 0x14 -#define TSL2772_ALS_CHAN0HI 0x15 -#define TSL2772_ALS_CHAN1LO 0x16 -#define TSL2772_ALS_CHAN1HI 0x17 -#define TSL2772_PRX_LO 0x18 -#define TSL2772_PRX_HI 0x19 - -/* tsl2772 cmd reg masks */ -#define TSL2772_CMD_REG 0x80 -#define TSL2772_CMD_SPL_FN 0x60 -#define TSL2772_CMD_REPEAT_PROTO 0x00 -#define TSL2772_CMD_AUTOINC_PROTO 0x20 - -#define TSL2772_CMD_PROX_INT_CLR 0X05 -#define TSL2772_CMD_ALS_INT_CLR 0x06 -#define TSL2772_CMD_PROXALS_INT_CLR 0X07 - -/* tsl2772 cntrl reg masks */ -#define TSL2772_CNTL_ADC_ENBL 0x02 -#define TSL2772_CNTL_PWR_ON 0x01 - -/* tsl2772 status reg masks */ -#define TSL2772_STA_ADC_VALID 0x01 -#define TSL2772_STA_PRX_VALID 0x02 -#define TSL2772_STA_ADC_PRX_VALID (TSL2772_STA_ADC_VALID | \ - TSL2772_STA_PRX_VALID) -#define TSL2772_STA_ALS_INTR 0x10 -#define TSL2772_STA_PRX_INTR 0x20 - -/* tsl2772 cntrl reg masks */ -#define TSL2772_CNTL_REG_CLEAR 0x00 -#define TSL2772_CNTL_PROX_INT_ENBL 0X20 -#define TSL2772_CNTL_ALS_INT_ENBL 0X10 -#define TSL2772_CNTL_WAIT_TMR_ENBL 0X08 -#define TSL2772_CNTL_PROX_DET_ENBL 0X04 -#define TSL2772_CNTL_PWRON 0x01 -#define TSL2772_CNTL_ALSPON_ENBL 0x03 -#define TSL2772_CNTL_INTALSPON_ENBL 0x13 -#define TSL2772_CNTL_PROXPON_ENBL 0x0F -#define TSL2772_CNTL_INTPROXPON_ENBL 0x2F - -#define TSL2772_ALS_GAIN_TRIM_MIN 250 -#define TSL2772_ALS_GAIN_TRIM_MAX 4000 - -/* Device family members */ -enum { - tsl2571, - tsl2671, - tmd2671, - tsl2771, - tmd2771, - tsl2572, - tsl2672, - tmd2672, - tsl2772, - tmd2772 -}; - -enum { - TSL2772_CHIP_UNKNOWN = 0, - TSL2772_CHIP_WORKING = 1, - TSL2772_CHIP_SUSPENDED = 2 -}; - -/* Per-device data */ -struct tsl2772_als_info { - u16 als_ch0; - u16 als_ch1; - u16 lux; -}; - -struct tsl2772_chip_info { - int chan_table_elements; - struct iio_chan_spec channel_with_events[4]; - struct iio_chan_spec channel_without_events[4]; - const struct iio_info *info; -}; - -struct tsl2772_chip { - kernel_ulong_t id; - struct mutex prox_mutex; - struct mutex als_mutex; - struct i2c_client *client; - u16 prox_data; - struct tsl2772_als_info als_cur_info; - struct tsl2772_settings settings; - struct tsl2772_platform_data *pdata; - int als_gain_time_scale; - int als_saturation; - int tsl2772_chip_status; - u8 tsl2772_config[TSL2772_MAX_CONFIG_REG]; - const struct tsl2772_chip_info *chip_info; - const struct iio_info *info; - s64 event_timestamp; - /* - * This structure is intentionally large to accommodate - * updates via sysfs. - * Sized to 9 = max 8 segments + 1 termination segment - */ - struct tsl2772_lux tsl2772_device_lux[TSL2772_MAX_LUX_TABLE_SIZE]; -}; - -/* - * Different devices require different coefficents, and these numbers were - * derived from the 'Lux Equation' section of the various device datasheets. - * All of these coefficients assume a Glass Attenuation (GA) factor of 1. - * The coefficients are multiplied by 1000 to avoid floating point operations. - * The two rows in each table correspond to the Lux1 and Lux2 equations from - * the datasheets. - */ -static const struct tsl2772_lux tsl2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 53000, 106000 }, - { 31800, 53000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux tmd2x71_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 24000, 48000 }, - { 14400, 24000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux tsl2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 60000, 112200 }, - { 37800, 60000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux tmd2x72_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = { - { 20000, 35000 }, - { 12600, 20000 }, - { 0, 0 }, -}; - -static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = { - [tsl2571] = tsl2x71_lux_table, - [tsl2671] = tsl2x71_lux_table, - [tmd2671] = tmd2x71_lux_table, - [tsl2771] = tsl2x71_lux_table, - [tmd2771] = tmd2x71_lux_table, - [tsl2572] = tsl2x72_lux_table, - [tsl2672] = tsl2x72_lux_table, - [tmd2672] = tmd2x72_lux_table, - [tsl2772] = tsl2x72_lux_table, - [tmd2772] = tmd2x72_lux_table, -}; - -static const struct tsl2772_settings tsl2772_default_settings = { - .als_time = 255, /* 2.72 / 2.73 ms */ - .als_gain = 0, - .prox_time = 255, /* 2.72 / 2.73 ms */ - .prox_gain = 0, - .wait_time = 255, - .als_prox_config = 0, - .als_gain_trim = 1000, - .als_cal_target = 150, - .als_persistence = 1, - .als_interrupt_en = false, - .als_thresh_low = 200, - .als_thresh_high = 256, - .prox_persistence = 1, - .prox_interrupt_en = false, - .prox_thres_low = 0, - .prox_thres_high = 512, - .prox_max_samples_cal = 30, - .prox_pulse_count = 8, - .prox_diode = TSL2772_DIODE1, - .prox_power = TSL2772_100_mA -}; - -static const s16 tsl2772_als_gain[] = { - 1, - 8, - 16, - 120 -}; - -static const s16 tsl2772_prox_gain[] = { - 1, - 2, - 4, - 8 -}; - -static const int tsl2772_int_time_avail[][6] = { - [tsl2571] = { 0, 2720, 0, 2720, 0, 696000 }, - [tsl2671] = { 0, 2720, 0, 2720, 0, 696000 }, - [tmd2671] = { 0, 2720, 0, 2720, 0, 696000 }, - [tsl2771] = { 0, 2720, 0, 2720, 0, 696000 }, - [tmd2771] = { 0, 2720, 0, 2720, 0, 696000 }, - [tsl2572] = { 0, 2730, 0, 2730, 0, 699000 }, - [tsl2672] = { 0, 2730, 0, 2730, 0, 699000 }, - [tmd2672] = { 0, 2730, 0, 2730, 0, 699000 }, - [tsl2772] = { 0, 2730, 0, 2730, 0, 699000 }, - [tmd2772] = { 0, 2730, 0, 2730, 0, 699000 }, -}; - -static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 }; - -static int tsl2772_prox_calibscale_avail[] = { 1, 2, 4, 8 }; - -/* Channel variations */ -enum { - ALS, - PRX, - ALSPRX, - PRX2, - ALSPRX2, -}; - -static const u8 device_channel_config[] = { - [tsl2571] = ALS, - [tsl2671] = PRX, - [tmd2671] = PRX, - [tsl2771] = ALSPRX, - [tmd2771] = ALSPRX, - [tsl2572] = ALS, - [tsl2672] = PRX2, - [tmd2672] = PRX2, - [tsl2772] = ALSPRX2, - [tmd2772] = ALSPRX2 -}; - -static int tsl2772_read_status(struct tsl2772_chip *chip) -{ - int ret; - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_STATUS); - if (ret < 0) - dev_err(&chip->client->dev, - "%s: failed to read STATUS register: %d\n", __func__, - ret); - - return ret; -} - -static int tsl2772_write_control_reg(struct tsl2772_chip *chip, u8 data) -{ - int ret; - - ret = i2c_smbus_write_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_CNTRL, data); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to write to control register %x: %d\n", - __func__, data, ret); - } - - return ret; -} - -static int tsl2772_read_autoinc_regs(struct tsl2772_chip *chip, int lower_reg, - int upper_reg) -{ - u8 buf[2]; - int ret; - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_AUTOINC_PROTO | - lower_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to enable auto increment protocol: %d\n", - __func__, ret); - return ret; - } - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | lower_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to read from register %x: %d\n", __func__, - lower_reg, ret); - return ret; - } - buf[0] = ret; - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | upper_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to read from register %x: %d\n", __func__, - upper_reg, ret); - return ret; - } - buf[1] = ret; - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_REPEAT_PROTO | - lower_reg); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to enable repeated byte protocol: %d\n", - __func__, ret); - return ret; - } - - return le16_to_cpup((const __le16 *)&buf[0]); -} - -/** - * tsl2772_get_lux() - Reads and calculates current lux value. - * @indio_dev: pointer to IIO device - * - * The raw ch0 and ch1 values of the ambient light sensed in the last - * integration cycle are read from the device. The raw values are multiplied - * by a device-specific scale factor, and divided by the integration time and - * device gain. The code supports multiple lux equations through the lux table - * coefficients. A lux gain trim is applied to each lux equation, and then the - * maximum lux within the interval 0..65535 is selected. - */ -static int tsl2772_get_lux(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - struct tsl2772_lux *p; - int max_lux, ret; - bool overflow; - - mutex_lock(&chip->als_mutex); - - if (chip->tsl2772_chip_status != TSL2772_CHIP_WORKING) { - dev_err(&chip->client->dev, "%s: device is not enabled\n", - __func__); - ret = -EBUSY; - goto out_unlock; - } - - ret = tsl2772_read_status(chip); - if (ret < 0) - goto out_unlock; - - if (!(ret & TSL2772_STA_ADC_VALID)) { - dev_err(&chip->client->dev, - "%s: data not valid yet\n", __func__); - ret = chip->als_cur_info.lux; /* return LAST VALUE */ - goto out_unlock; - } - - ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN0LO, - TSL2772_ALS_CHAN0HI); - if (ret < 0) - goto out_unlock; - chip->als_cur_info.als_ch0 = ret; - - ret = tsl2772_read_autoinc_regs(chip, TSL2772_ALS_CHAN1LO, - TSL2772_ALS_CHAN1HI); - if (ret < 0) - goto out_unlock; - chip->als_cur_info.als_ch1 = ret; - - if (chip->als_cur_info.als_ch0 >= chip->als_saturation) { - max_lux = TSL2772_LUX_CALC_OVER_FLOW; - goto update_struct_with_max_lux; - } - - if (!chip->als_cur_info.als_ch0) { - /* have no data, so return LAST VALUE */ - ret = chip->als_cur_info.lux; - goto out_unlock; - } - - max_lux = 0; - overflow = false; - for (p = (struct tsl2772_lux *)chip->tsl2772_device_lux; p->ch0 != 0; - p++) { - int lux; - - lux = ((chip->als_cur_info.als_ch0 * p->ch0) - - (chip->als_cur_info.als_ch1 * p->ch1)) / - chip->als_gain_time_scale; - - /* - * The als_gain_trim can have a value within the range 250..4000 - * and is a multiplier for the lux. A trim of 1000 makes no - * changes to the lux, less than 1000 scales it down, and - * greater than 1000 scales it up. - */ - lux = (lux * chip->settings.als_gain_trim) / 1000; - - if (lux > TSL2772_LUX_CALC_OVER_FLOW) { - overflow = true; - continue; - } - - max_lux = max(max_lux, lux); - } - - if (overflow && max_lux == 0) - max_lux = TSL2772_LUX_CALC_OVER_FLOW; - -update_struct_with_max_lux: - chip->als_cur_info.lux = max_lux; - ret = max_lux; - -out_unlock: - mutex_unlock(&chip->als_mutex); - - return ret; -} - -/** - * tsl2772_get_prox() - Reads proximity data registers and updates - * chip->prox_data. - * - * @indio_dev: pointer to IIO device - */ -static int tsl2772_get_prox(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret; - - mutex_lock(&chip->prox_mutex); - - ret = tsl2772_read_status(chip); - if (ret < 0) - goto prox_poll_err; - - switch (chip->id) { - case tsl2571: - case tsl2671: - case tmd2671: - case tsl2771: - case tmd2771: - if (!(ret & TSL2772_STA_ADC_VALID)) { - ret = -EINVAL; - goto prox_poll_err; - } - break; - case tsl2572: - case tsl2672: - case tmd2672: - case tsl2772: - case tmd2772: - if (!(ret & TSL2772_STA_PRX_VALID)) { - ret = -EINVAL; - goto prox_poll_err; - } - break; - } - - ret = tsl2772_read_autoinc_regs(chip, TSL2772_PRX_LO, TSL2772_PRX_HI); - if (ret < 0) - goto prox_poll_err; - chip->prox_data = ret; - -prox_poll_err: - mutex_unlock(&chip->prox_mutex); - - return ret; -} - -/** - * tsl2772_defaults() - Populates the device nominal operating parameters - * with those provided by a 'platform' data struct or - * with prefined defaults. - * - * @chip: pointer to device structure. - */ -static void tsl2772_defaults(struct tsl2772_chip *chip) -{ - /* If Operational settings defined elsewhere.. */ - if (chip->pdata && chip->pdata->platform_default_settings) - memcpy(&chip->settings, chip->pdata->platform_default_settings, - sizeof(tsl2772_default_settings)); - else - memcpy(&chip->settings, &tsl2772_default_settings, - sizeof(tsl2772_default_settings)); - - /* Load up the proper lux table. */ - if (chip->pdata && chip->pdata->platform_lux_table[0].ch0 != 0) - memcpy(chip->tsl2772_device_lux, - chip->pdata->platform_lux_table, - sizeof(chip->pdata->platform_lux_table)); - else - memcpy(chip->tsl2772_device_lux, - tsl2772_default_lux_table_group[chip->id], - TSL2772_DEFAULT_TABLE_BYTES); -} - -/** - * tsl2772_als_calibrate() - Obtain single reading and calculate - * the als_gain_trim. - * - * @indio_dev: pointer to IIO device - */ -static int tsl2772_als_calibrate(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret, lux_val; - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_CNTRL); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to read from the CNTRL register\n", - __func__); - return ret; - } - - if ((ret & (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) - != (TSL2772_CNTL_ADC_ENBL | TSL2772_CNTL_PWR_ON)) { - dev_err(&chip->client->dev, - "%s: Device is not powered on and/or ADC is not enabled\n", - __func__); - return -EINVAL; - } else if ((ret & TSL2772_STA_ADC_VALID) != TSL2772_STA_ADC_VALID) { - dev_err(&chip->client->dev, - "%s: The two ADC channels have not completed an integration cycle\n", - __func__); - return -ENODATA; - } - - lux_val = tsl2772_get_lux(indio_dev); - if (lux_val < 0) { - dev_err(&chip->client->dev, - "%s: failed to get lux\n", __func__); - return lux_val; - } - - ret = (chip->settings.als_cal_target * chip->settings.als_gain_trim) / - lux_val; - if (ret < TSL2772_ALS_GAIN_TRIM_MIN || ret > TSL2772_ALS_GAIN_TRIM_MAX) - return -ERANGE; - - chip->settings.als_gain_trim = ret; - - return ret; -} - -static int tsl2772_chip_on(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret, i, als_count, als_time_us; - u8 *dev_reg, reg_val; - - /* Non calculated parameters */ - chip->tsl2772_config[TSL2772_ALS_TIME] = chip->settings.als_time; - chip->tsl2772_config[TSL2772_PRX_TIME] = chip->settings.prox_time; - chip->tsl2772_config[TSL2772_WAIT_TIME] = chip->settings.wait_time; - chip->tsl2772_config[TSL2772_ALS_PRX_CONFIG] = - chip->settings.als_prox_config; - - chip->tsl2772_config[TSL2772_ALS_MINTHRESHLO] = - (chip->settings.als_thresh_low) & 0xFF; - chip->tsl2772_config[TSL2772_ALS_MINTHRESHHI] = - (chip->settings.als_thresh_low >> 8) & 0xFF; - chip->tsl2772_config[TSL2772_ALS_MAXTHRESHLO] = - (chip->settings.als_thresh_high) & 0xFF; - chip->tsl2772_config[TSL2772_ALS_MAXTHRESHHI] = - (chip->settings.als_thresh_high >> 8) & 0xFF; - chip->tsl2772_config[TSL2772_PERSISTENCE] = - (chip->settings.prox_persistence & 0xFF) << 4 | - (chip->settings.als_persistence & 0xFF); - - chip->tsl2772_config[TSL2772_PRX_COUNT] = - chip->settings.prox_pulse_count; - chip->tsl2772_config[TSL2772_PRX_MINTHRESHLO] = - (chip->settings.prox_thres_low) & 0xFF; - chip->tsl2772_config[TSL2772_PRX_MINTHRESHHI] = - (chip->settings.prox_thres_low >> 8) & 0xFF; - chip->tsl2772_config[TSL2772_PRX_MAXTHRESHLO] = - (chip->settings.prox_thres_high) & 0xFF; - chip->tsl2772_config[TSL2772_PRX_MAXTHRESHHI] = - (chip->settings.prox_thres_high >> 8) & 0xFF; - - /* and make sure we're not already on */ - if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { - /* if forcing a register update - turn off, then on */ - dev_info(&chip->client->dev, "device is already enabled\n"); - return -EINVAL; - } - - /* Set the gain based on tsl2772_settings struct */ - chip->tsl2772_config[TSL2772_GAIN] = - (chip->settings.als_gain & 0xFF) | - ((chip->settings.prox_gain & 0xFF) << 2) | - (chip->settings.prox_diode << 4) | - (chip->settings.prox_power << 6); - - /* set chip time scaling and saturation */ - als_count = 256 - chip->settings.als_time; - als_time_us = als_count * tsl2772_int_time_avail[chip->id][3]; - chip->als_saturation = als_count * 768; /* 75% of full scale */ - chip->als_gain_time_scale = als_time_us * - tsl2772_als_gain[chip->settings.als_gain]; - - /* - * TSL2772 Specific power-on / adc enable sequence - * Power on the device 1st. - */ - ret = tsl2772_write_control_reg(chip, TSL2772_CNTL_PWR_ON); - if (ret < 0) - return ret; - - /* - * Use the following shadow copy for our delay before enabling ADC. - * Write all the registers. - */ - for (i = 0, dev_reg = chip->tsl2772_config; - i < TSL2772_MAX_CONFIG_REG; i++) { - int reg = TSL2772_CMD_REG + i; - - ret = i2c_smbus_write_byte_data(chip->client, reg, - *dev_reg++); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to write to register %x: %d\n", - __func__, reg, ret); - return ret; - } - } - - /* Power-on settling time */ - usleep_range(3000, 3500); - - reg_val = TSL2772_CNTL_PWR_ON | TSL2772_CNTL_ADC_ENBL | - TSL2772_CNTL_PROX_DET_ENBL; - if (chip->settings.als_interrupt_en) - reg_val |= TSL2772_CNTL_ALS_INT_ENBL; - if (chip->settings.prox_interrupt_en) - reg_val |= TSL2772_CNTL_PROX_INT_ENBL; - - ret = tsl2772_write_control_reg(chip, reg_val); - if (ret < 0) - return ret; - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | - TSL2772_CMD_PROXALS_INT_CLR); - if (ret < 0) { - dev_err(&chip->client->dev, - "%s: failed to clear interrupt status: %d\n", - __func__, ret); - return ret; - } - - chip->tsl2772_chip_status = TSL2772_CHIP_WORKING; - - return ret; -} - -static int tsl2772_chip_off(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - /* turn device off */ - chip->tsl2772_chip_status = TSL2772_CHIP_SUSPENDED; - return tsl2772_write_control_reg(chip, 0x00); -} - -/** - * tsl2772_invoke_change - power cycle the device to implement the user - * parameters - * @indio_dev: pointer to IIO device - * - * Obtain and lock both ALS and PROX resources, determine and save device state - * (On/Off), cycle device to implement updated parameter, put device back into - * proper state, and unlock resource. - */ -static int tsl2772_invoke_change(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int device_status = chip->tsl2772_chip_status; - int ret; - - mutex_lock(&chip->als_mutex); - mutex_lock(&chip->prox_mutex); - - if (device_status == TSL2772_CHIP_WORKING) { - ret = tsl2772_chip_off(indio_dev); - if (ret < 0) - goto unlock; - } - - ret = tsl2772_chip_on(indio_dev); - -unlock: - mutex_unlock(&chip->prox_mutex); - mutex_unlock(&chip->als_mutex); - - return ret; -} - -static int tsl2772_prox_cal(struct iio_dev *indio_dev) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int prox_history[MAX_SAMPLES_CAL + 1]; - int i, ret, mean, max, sample_sum; - - if (chip->settings.prox_max_samples_cal < 1 || - chip->settings.prox_max_samples_cal > MAX_SAMPLES_CAL) - return -EINVAL; - - for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { - usleep_range(15000, 17500); - ret = tsl2772_get_prox(indio_dev); - if (ret < 0) - return ret; - - prox_history[i] = chip->prox_data; - } - - sample_sum = 0; - max = INT_MIN; - for (i = 0; i < chip->settings.prox_max_samples_cal; i++) { - sample_sum += prox_history[i]; - max = max(max, prox_history[i]); - } - mean = sample_sum / chip->settings.prox_max_samples_cal; - - chip->settings.prox_thres_high = (max << 1) - mean; - - return tsl2772_invoke_change(indio_dev); -} - -static int tsl2772_read_avail(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - const int **vals, int *type, int *length, - long mask) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_INTENSITY) { - *length = ARRAY_SIZE(tsl2772_int_calibscale_avail); - *vals = tsl2772_int_calibscale_avail; - } else { - *length = ARRAY_SIZE(tsl2772_prox_calibscale_avail); - *vals = tsl2772_prox_calibscale_avail; - } - *type = IIO_VAL_INT; - return IIO_AVAIL_LIST; - case IIO_CHAN_INFO_INT_TIME: - *length = ARRAY_SIZE(tsl2772_int_time_avail[chip->id]); - *vals = tsl2772_int_time_avail[chip->id]; - *type = IIO_VAL_INT_PLUS_MICRO; - return IIO_AVAIL_RANGE; - } - - return -EINVAL; -} - -static ssize_t in_illuminance0_target_input_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); - - return snprintf(buf, PAGE_SIZE, "%d\n", chip->settings.als_cal_target); -} - -static ssize_t in_illuminance0_target_input_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct tsl2772_chip *chip = iio_priv(indio_dev); - u16 value; - int ret; - - if (kstrtou16(buf, 0, &value)) - return -EINVAL; - - chip->settings.als_cal_target = value; - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static ssize_t in_illuminance0_calibrate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - bool value; - int ret; - - if (kstrtobool(buf, &value) || !value) - return -EINVAL; - - ret = tsl2772_als_calibrate(indio_dev); - if (ret < 0) - return ret; - - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static ssize_t in_illuminance0_lux_table_show(struct device *dev, - struct device_attribute *attr, - char *buf) -{ - struct tsl2772_chip *chip = iio_priv(dev_to_iio_dev(dev)); - int i = 0; - int offset = 0; - - while (i < TSL2772_MAX_LUX_TABLE_SIZE) { - offset += snprintf(buf + offset, PAGE_SIZE, "%u,%u,", - chip->tsl2772_device_lux[i].ch0, - chip->tsl2772_device_lux[i].ch1); - if (chip->tsl2772_device_lux[i].ch0 == 0) { - /* - * We just printed the first "0" entry. - * Now get rid of the extra "," and break. - */ - offset--; - break; - } - i++; - } - - offset += snprintf(buf + offset, PAGE_SIZE, "\n"); - return offset; -} - -static ssize_t in_illuminance0_lux_table_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct tsl2772_chip *chip = iio_priv(indio_dev); - int value[ARRAY_SIZE(chip->tsl2772_device_lux) * 2 + 1]; - int n, ret; - - get_options(buf, ARRAY_SIZE(value), value); - - /* - * We now have an array of ints starting at value[1], and - * enumerated by value[0]. - * We expect each group of two ints to be one table entry, - * and the last table entry is all 0. - */ - n = value[0]; - if ((n % 2) || n < 4 || - n > ((ARRAY_SIZE(chip->tsl2772_device_lux) - 1) * 2)) - return -EINVAL; - - if ((value[(n - 1)] | value[n]) != 0) - return -EINVAL; - - if (chip->tsl2772_chip_status == TSL2772_CHIP_WORKING) { - ret = tsl2772_chip_off(indio_dev); - if (ret < 0) - return ret; - } - - /* Zero out the table */ - memset(chip->tsl2772_device_lux, 0, sizeof(chip->tsl2772_device_lux)); - memcpy(chip->tsl2772_device_lux, &value[1], (value[0] * 4)); - - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static ssize_t in_proximity0_calibrate_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) -{ - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - bool value; - int ret; - - if (kstrtobool(buf, &value) || !value) - return -EINVAL; - - ret = tsl2772_prox_cal(indio_dev); - if (ret < 0) - return ret; - - ret = tsl2772_invoke_change(indio_dev); - if (ret < 0) - return ret; - - return len; -} - -static int tsl2772_read_interrupt_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - if (chan->type == IIO_INTENSITY) - return chip->settings.als_interrupt_en; - else - return chip->settings.prox_interrupt_en; -} - -static int tsl2772_write_interrupt_config(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - int val) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - if (chan->type == IIO_INTENSITY) - chip->settings.als_interrupt_en = val ? true : false; - else - chip->settings.prox_interrupt_en = val ? true : false; - - return tsl2772_invoke_change(indio_dev); -} - -static int tsl2772_write_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int val, int val2) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int ret = -EINVAL, count, persistence; - u8 time; - - switch (info) { - case IIO_EV_INFO_VALUE: - if (chan->type == IIO_INTENSITY) { - switch (dir) { - case IIO_EV_DIR_RISING: - chip->settings.als_thresh_high = val; - ret = 0; - break; - case IIO_EV_DIR_FALLING: - chip->settings.als_thresh_low = val; - ret = 0; - break; - default: - break; - } - } else { - switch (dir) { - case IIO_EV_DIR_RISING: - chip->settings.prox_thres_high = val; - ret = 0; - break; - case IIO_EV_DIR_FALLING: - chip->settings.prox_thres_low = val; - ret = 0; - break; - default: - break; - } - } - break; - case IIO_EV_INFO_PERIOD: - if (chan->type == IIO_INTENSITY) - time = chip->settings.als_time; - else - time = chip->settings.prox_time; - - count = 256 - time; - persistence = ((val * 1000000) + val2) / - (count * tsl2772_int_time_avail[chip->id][3]); - - if (chan->type == IIO_INTENSITY) { - /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ - if (persistence > 3) - persistence = (persistence / 5) + 3; - - chip->settings.als_persistence = persistence; - } else { - chip->settings.prox_persistence = persistence; - } - - ret = 0; - break; - default: - break; - } - - if (ret < 0) - return ret; - - return tsl2772_invoke_change(indio_dev); -} - -static int tsl2772_read_event_value(struct iio_dev *indio_dev, - const struct iio_chan_spec *chan, - enum iio_event_type type, - enum iio_event_direction dir, - enum iio_event_info info, - int *val, int *val2) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - int filter_delay, persistence; - u8 time; - - switch (info) { - case IIO_EV_INFO_VALUE: - if (chan->type == IIO_INTENSITY) { - switch (dir) { - case IIO_EV_DIR_RISING: - *val = chip->settings.als_thresh_high; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - *val = chip->settings.als_thresh_low; - return IIO_VAL_INT; - default: - return -EINVAL; - } - } else { - switch (dir) { - case IIO_EV_DIR_RISING: - *val = chip->settings.prox_thres_high; - return IIO_VAL_INT; - case IIO_EV_DIR_FALLING: - *val = chip->settings.prox_thres_low; - return IIO_VAL_INT; - default: - return -EINVAL; - } - } - break; - case IIO_EV_INFO_PERIOD: - if (chan->type == IIO_INTENSITY) { - time = chip->settings.als_time; - persistence = chip->settings.als_persistence; - - /* ALS filter values are 1, 2, 3, 5, 10, 15, ..., 60 */ - if (persistence > 3) - persistence = (persistence - 3) * 5; - } else { - time = chip->settings.prox_time; - persistence = chip->settings.prox_persistence; - } - - filter_delay = persistence * (256 - time) * - tsl2772_int_time_avail[chip->id][3]; - - *val = filter_delay / 1000000; - *val2 = filter_delay % 1000000; - return IIO_VAL_INT_PLUS_MICRO; - default: - return -EINVAL; - } -} - -static int tsl2772_read_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int *val, - int *val2, - long mask) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_PROCESSED: - switch (chan->type) { - case IIO_LIGHT: - tsl2772_get_lux(indio_dev); - *val = chip->als_cur_info.lux; - return IIO_VAL_INT; - default: - return -EINVAL; - } - case IIO_CHAN_INFO_RAW: - switch (chan->type) { - case IIO_INTENSITY: - tsl2772_get_lux(indio_dev); - if (chan->channel == 0) - *val = chip->als_cur_info.als_ch0; - else - *val = chip->als_cur_info.als_ch1; - return IIO_VAL_INT; - case IIO_PROXIMITY: - tsl2772_get_prox(indio_dev); - *val = chip->prox_data; - return IIO_VAL_INT; - default: - return -EINVAL; - } - break; - case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_LIGHT) - *val = tsl2772_als_gain[chip->settings.als_gain]; - else - *val = tsl2772_prox_gain[chip->settings.prox_gain]; - return IIO_VAL_INT; - case IIO_CHAN_INFO_CALIBBIAS: - *val = chip->settings.als_gain_trim; - return IIO_VAL_INT; - case IIO_CHAN_INFO_INT_TIME: - *val = 0; - *val2 = (256 - chip->settings.als_time) * - tsl2772_int_time_avail[chip->id][3]; - return IIO_VAL_INT_PLUS_MICRO; - default: - return -EINVAL; - } -} - -static int tsl2772_write_raw(struct iio_dev *indio_dev, - struct iio_chan_spec const *chan, - int val, - int val2, - long mask) -{ - struct tsl2772_chip *chip = iio_priv(indio_dev); - - switch (mask) { - case IIO_CHAN_INFO_CALIBSCALE: - if (chan->type == IIO_INTENSITY) { - switch (val) { - case 1: - chip->settings.als_gain = 0; - break; - case 8: - chip->settings.als_gain = 1; - break; - case 16: - chip->settings.als_gain = 2; - break; - case 120: - chip->settings.als_gain = 3; - break; - default: - return -EINVAL; - } - } else { - switch (val) { - case 1: - chip->settings.prox_gain = 0; - break; - case 2: - chip->settings.prox_gain = 1; - break; - case 4: - chip->settings.prox_gain = 2; - break; - case 8: - chip->settings.prox_gain = 3; - break; - default: - return -EINVAL; - } - } - break; - case IIO_CHAN_INFO_CALIBBIAS: - if (val < TSL2772_ALS_GAIN_TRIM_MIN || - val > TSL2772_ALS_GAIN_TRIM_MAX) - return -EINVAL; - - chip->settings.als_gain_trim = val; - break; - case IIO_CHAN_INFO_INT_TIME: - if (val != 0 || val2 < tsl2772_int_time_avail[chip->id][1] || - val2 > tsl2772_int_time_avail[chip->id][5]) - return -EINVAL; - - chip->settings.als_time = 256 - - (val2 / tsl2772_int_time_avail[chip->id][3]); - break; - default: - return -EINVAL; - } - - return tsl2772_invoke_change(indio_dev); -} - -static DEVICE_ATTR_RW(in_illuminance0_target_input); - -static DEVICE_ATTR_WO(in_illuminance0_calibrate); - -static DEVICE_ATTR_WO(in_proximity0_calibrate); - -static DEVICE_ATTR_RW(in_illuminance0_lux_table); - -/* Use the default register values to identify the Taos device */ -static int tsl2772_device_id_verif(int id, int target) -{ - switch (target) { - case tsl2571: - case tsl2671: - case tsl2771: - return (id & 0xf0) == TRITON_ID; - case tmd2671: - case tmd2771: - return (id & 0xf0) == HALIBUT_ID; - case tsl2572: - case tsl2672: - case tmd2672: - case tsl2772: - case tmd2772: - return (id & 0xf0) == SWORDFISH_ID; - } - - return -EINVAL; -} - -static irqreturn_t tsl2772_event_handler(int irq, void *private) -{ - struct iio_dev *indio_dev = private; - struct tsl2772_chip *chip = iio_priv(indio_dev); - s64 timestamp = iio_get_time_ns(indio_dev); - int ret; - - ret = tsl2772_read_status(chip); - if (ret < 0) - return IRQ_HANDLED; - - /* What type of interrupt do we need to process */ - if (ret & TSL2772_STA_PRX_INTR) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_EITHER), - timestamp); - } - - if (ret & TSL2772_STA_ALS_INTR) { - iio_push_event(indio_dev, - IIO_UNMOD_EVENT_CODE(IIO_LIGHT, - 0, - IIO_EV_TYPE_THRESH, - IIO_EV_DIR_EITHER), - timestamp); - } - - ret = i2c_smbus_write_byte(chip->client, - TSL2772_CMD_REG | TSL2772_CMD_SPL_FN | - TSL2772_CMD_PROXALS_INT_CLR); - if (ret < 0) - dev_err(&chip->client->dev, - "%s: failed to clear interrupt status: %d\n", - __func__, ret); - - return IRQ_HANDLED; -} - -static struct attribute *tsl2772_ALS_device_attrs[] = { - &dev_attr_in_illuminance0_target_input.attr, - &dev_attr_in_illuminance0_calibrate.attr, - &dev_attr_in_illuminance0_lux_table.attr, - NULL -}; - -static struct attribute *tsl2772_PRX_device_attrs[] = { - &dev_attr_in_proximity0_calibrate.attr, - NULL -}; - -static struct attribute *tsl2772_ALSPRX_device_attrs[] = { - &dev_attr_in_illuminance0_target_input.attr, - &dev_attr_in_illuminance0_calibrate.attr, - &dev_attr_in_illuminance0_lux_table.attr, - NULL -}; - -static struct attribute *tsl2772_PRX2_device_attrs[] = { - &dev_attr_in_proximity0_calibrate.attr, - NULL -}; - -static struct attribute *tsl2772_ALSPRX2_device_attrs[] = { - &dev_attr_in_illuminance0_target_input.attr, - &dev_attr_in_illuminance0_calibrate.attr, - &dev_attr_in_illuminance0_lux_table.attr, - &dev_attr_in_proximity0_calibrate.attr, - NULL -}; - -static const struct attribute_group tsl2772_device_attr_group_tbl[] = { - [ALS] = { - .attrs = tsl2772_ALS_device_attrs, - }, - [PRX] = { - .attrs = tsl2772_PRX_device_attrs, - }, - [ALSPRX] = { - .attrs = tsl2772_ALSPRX_device_attrs, - }, - [PRX2] = { - .attrs = tsl2772_PRX2_device_attrs, - }, - [ALSPRX2] = { - .attrs = tsl2772_ALSPRX2_device_attrs, - }, -}; - -#define TSL2772_DEVICE_INFO(type)[type] = \ - { \ - .attrs = &tsl2772_device_attr_group_tbl[type], \ - .read_raw = &tsl2772_read_raw, \ - .read_avail = &tsl2772_read_avail, \ - .write_raw = &tsl2772_write_raw, \ - .read_event_value = &tsl2772_read_event_value, \ - .write_event_value = &tsl2772_write_event_value, \ - .read_event_config = &tsl2772_read_interrupt_config, \ - .write_event_config = &tsl2772_write_interrupt_config, \ - } - -static const struct iio_info tsl2772_device_info[] = { - TSL2772_DEVICE_INFO(ALS), - TSL2772_DEVICE_INFO(PRX), - TSL2772_DEVICE_INFO(ALSPRX), - TSL2772_DEVICE_INFO(PRX2), - TSL2772_DEVICE_INFO(ALSPRX2), -}; - -static const struct iio_event_spec tsl2772_events[] = { - { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_RISING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_FALLING, - .mask_separate = BIT(IIO_EV_INFO_VALUE), - }, { - .type = IIO_EV_TYPE_THRESH, - .dir = IIO_EV_DIR_EITHER, - .mask_separate = BIT(IIO_EV_INFO_PERIOD) | - BIT(IIO_EV_INFO_ENABLE), - }, -}; - -static const struct tsl2772_chip_info tsl2772_chip_info_tbl[] = { - [ALS] = { - .channel_with_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - }, - }, - .channel_without_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - }, - }, - .chan_table_elements = 3, - .info = &tsl2772_device_info[ALS], - }, - [PRX] = { - .channel_with_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, - }, - .chan_table_elements = 1, - .info = &tsl2772_device_info[PRX], - }, - [ALSPRX] = { - .channel_with_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, - }, - .chan_table_elements = 4, - .info = &tsl2772_device_info[ALSPRX], - }, - [PRX2] = { - .channel_with_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, - }, - .chan_table_elements = 1, - .info = &tsl2772_device_info[PRX2], - }, - [ALSPRX2] = { - .channel_with_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - .event_spec = tsl2772_events, - .num_event_specs = ARRAY_SIZE(tsl2772_events), - }, - }, - .channel_without_events = { - { - .type = IIO_LIGHT, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE) | - BIT(IIO_CHAN_INFO_CALIBBIAS), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_INT_TIME) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, { - .type = IIO_INTENSITY, - .indexed = 1, - .channel = 1, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), - }, { - .type = IIO_PROXIMITY, - .indexed = 1, - .channel = 0, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | - BIT(IIO_CHAN_INFO_CALIBSCALE), - .info_mask_separate_available = - BIT(IIO_CHAN_INFO_CALIBSCALE), - }, - }, - .chan_table_elements = 4, - .info = &tsl2772_device_info[ALSPRX2], - }, -}; - -static int tsl2772_probe(struct i2c_client *clientp, - const struct i2c_device_id *id) -{ - struct iio_dev *indio_dev; - struct tsl2772_chip *chip; - int ret; - - indio_dev = devm_iio_device_alloc(&clientp->dev, sizeof(*chip)); - if (!indio_dev) - return -ENOMEM; - - chip = iio_priv(indio_dev); - chip->client = clientp; - i2c_set_clientdata(clientp, indio_dev); - - ret = i2c_smbus_read_byte_data(chip->client, - TSL2772_CMD_REG | TSL2772_CHIPID); - if (ret < 0) - return ret; - - if (tsl2772_device_id_verif(ret, id->driver_data) <= 0) { - dev_info(&chip->client->dev, - "%s: i2c device found does not match expected id\n", - __func__); - return -EINVAL; - } - - ret = i2c_smbus_write_byte(clientp, TSL2772_CMD_REG | TSL2772_CNTRL); - if (ret < 0) { - dev_err(&clientp->dev, - "%s: Failed to write to CMD register: %d\n", - __func__, ret); - return ret; - } - - mutex_init(&chip->als_mutex); - mutex_init(&chip->prox_mutex); - - chip->tsl2772_chip_status = TSL2772_CHIP_UNKNOWN; - chip->pdata = dev_get_platdata(&clientp->dev); - chip->id = id->driver_data; - chip->chip_info = - &tsl2772_chip_info_tbl[device_channel_config[id->driver_data]]; - - indio_dev->info = chip->chip_info->info; - indio_dev->dev.parent = &clientp->dev; - indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->name = chip->client->name; - indio_dev->num_channels = chip->chip_info->chan_table_elements; - - if (clientp->irq) { - indio_dev->channels = chip->chip_info->channel_with_events; - - ret = devm_request_threaded_irq(&clientp->dev, clientp->irq, - NULL, - &tsl2772_event_handler, - IRQF_TRIGGER_FALLING | - IRQF_ONESHOT, - "TSL2772_event", - indio_dev); - if (ret) { - dev_err(&clientp->dev, - "%s: irq request failed\n", __func__); - return ret; - } - } else { - indio_dev->channels = chip->chip_info->channel_without_events; - } - - tsl2772_defaults(chip); - ret = tsl2772_chip_on(indio_dev); - if (ret < 0) - return ret; - - ret = iio_device_register(indio_dev); - if (ret) { - tsl2772_chip_off(indio_dev); - dev_err(&clientp->dev, - "%s: iio registration failed\n", __func__); - return ret; - } - - return 0; -} - -static int tsl2772_suspend(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - - return tsl2772_chip_off(indio_dev); -} - -static int tsl2772_resume(struct device *dev) -{ - struct iio_dev *indio_dev = dev_get_drvdata(dev); - - return tsl2772_chip_on(indio_dev); -} - -static int tsl2772_remove(struct i2c_client *client) -{ - struct iio_dev *indio_dev = i2c_get_clientdata(client); - - tsl2772_chip_off(indio_dev); - - iio_device_unregister(indio_dev); - - return 0; -} - -static const struct i2c_device_id tsl2772_idtable[] = { - { "tsl2571", tsl2571 }, - { "tsl2671", tsl2671 }, - { "tmd2671", tmd2671 }, - { "tsl2771", tsl2771 }, - { "tmd2771", tmd2771 }, - { "tsl2572", tsl2572 }, - { "tsl2672", tsl2672 }, - { "tmd2672", tmd2672 }, - { "tsl2772", tsl2772 }, - { "tmd2772", tmd2772 }, - {} -}; - -MODULE_DEVICE_TABLE(i2c, tsl2772_idtable); - -static const struct of_device_id tsl2772_of_match[] = { - { .compatible = "amstaos,tsl2571" }, - { .compatible = "amstaos,tsl2671" }, - { .compatible = "amstaos,tmd2671" }, - { .compatible = "amstaos,tsl2771" }, - { .compatible = "amstaos,tmd2771" }, - { .compatible = "amstaos,tsl2572" }, - { .compatible = "amstaos,tsl2672" }, - { .compatible = "amstaos,tmd2672" }, - { .compatible = "amstaos,tsl2772" }, - { .compatible = "amstaos,tmd2772" }, - {} -}; -MODULE_DEVICE_TABLE(of, tsl2772_of_match); - -static const struct dev_pm_ops tsl2772_pm_ops = { - .suspend = tsl2772_suspend, - .resume = tsl2772_resume, -}; - -static struct i2c_driver tsl2772_driver = { - .driver = { - .name = "tsl2772", - .of_match_table = tsl2772_of_match, - .pm = &tsl2772_pm_ops, - }, - .id_table = tsl2772_idtable, - .probe = tsl2772_probe, - .remove = tsl2772_remove, -}; - -module_i2c_driver(tsl2772_driver); - -MODULE_AUTHOR("J. August Brenner "); -MODULE_AUTHOR("Brian Masney "); -MODULE_DESCRIPTION("TAOS tsl2772 ambient and proximity light sensor driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/iio/light/tsl2772.h b/drivers/staging/iio/light/tsl2772.h deleted file mode 100644 index f8ade15a35e2..000000000000 --- a/drivers/staging/iio/light/tsl2772.h +++ /dev/null @@ -1,101 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Device driver for monitoring ambient light intensity (lux) - * and proximity (prox) within the TAOS TSL2772 family of devices. - * - * Copyright (c) 2012, TAOS Corporation. - * Copyright (c) 2017-2018 Brian Masney - */ - -#ifndef __TSL2772_H -#define __TSL2772_H - -struct tsl2772_lux { - unsigned int ch0; - unsigned int ch1; -}; - -/* Max number of segments allowable in LUX table */ -#define TSL2772_MAX_LUX_TABLE_SIZE 6 -/* The default LUX tables all have 3 elements. */ -#define TSL2772_DEF_LUX_TABLE_SZ 3 -#define TSL2772_DEFAULT_TABLE_BYTES (sizeof(struct tsl2772_lux) * \ - TSL2772_DEF_LUX_TABLE_SZ) - -/* Proximity diode to use */ -#define TSL2772_DIODE0 0x01 -#define TSL2772_DIODE1 0x02 -#define TSL2772_DIODE_BOTH 0x03 - -/* LED Power */ -#define TSL2772_100_mA 0x00 -#define TSL2772_50_mA 0x01 -#define TSL2772_25_mA 0x02 -#define TSL2772_13_mA 0x03 - -/** - * struct tsl2772_settings - Settings for the tsl2772 driver - * @als_time: Integration time of the ALS channel ADCs in 2.73 ms - * increments. Total integration time is - * (256 - als_time) * 2.73. - * @als_gain: Index into the tsl2772_als_gain array. - * @als_gain_trim: Default gain trim to account for aperture effects. - * @wait_time: Time between proximity and ALS cycles in 2.73 - * periods. - * @prox_time: Integration time of the proximity ADC in 2.73 ms - * increments. Total integration time is - * (256 - prx_time) * 2.73. - * @prox_gain: Index into the tsl2772_prx_gain array. - * @als_prox_config: The value of the ALS / Proximity configuration - * register. - * @als_cal_target: Known external ALS reading for calibration. - * @als_persistence: H/W Filters, Number of 'out of limits' ALS readings. - * @als_interrupt_en: Enable/Disable ALS interrupts - * @als_thresh_low: CH0 'low' count to trigger interrupt. - * @als_thresh_high: CH0 'high' count to trigger interrupt. - * @prox_persistence: H/W Filters, Number of 'out of limits' proximity - * readings. - * @prox_interrupt_en: Enable/Disable proximity interrupts. - * @prox_thres_low: Low threshold proximity detection. - * @prox_thres_high: High threshold proximity detection. - * @prox_pulse_count: Number if proximity emitter pulses. - * @prox_max_samples_cal: The number of samples that are taken when performing - * a proximity calibration. - * @prox_diode Which diode(s) to use for driving the external - * LED(s) for proximity sensing. - * @prox_power The amount of power to use for the external LED(s). - */ -struct tsl2772_settings { - int als_time; - int als_gain; - int als_gain_trim; - int wait_time; - int prox_time; - int prox_gain; - int als_prox_config; - int als_cal_target; - u8 als_persistence; - bool als_interrupt_en; - int als_thresh_low; - int als_thresh_high; - u8 prox_persistence; - bool prox_interrupt_en; - int prox_thres_low; - int prox_thres_high; - int prox_pulse_count; - int prox_max_samples_cal; - int prox_diode; - int prox_power; -}; - -/** - * struct tsl2772_platform_data - Platform callback, glass and defaults - * @platform_lux_table: Device specific glass coefficents - * @platform_default_settings: Device specific power on defaults - */ -struct tsl2772_platform_data { - struct tsl2772_lux platform_lux_table[TSL2772_MAX_LUX_TABLE_SIZE]; - struct tsl2772_settings *platform_default_settings; -}; - -#endif /* __TSL2772_H */ diff --git a/include/linux/platform_data/tsl2772.h b/include/linux/platform_data/tsl2772.h new file mode 100644 index 000000000000..f8ade15a35e2 --- /dev/null +++ b/include/linux/platform_data/tsl2772.h @@ -0,0 +1,101 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Device driver for monitoring ambient light intensity (lux) + * and proximity (prox) within the TAOS TSL2772 family of devices. + * + * Copyright (c) 2012, TAOS Corporation. + * Copyright (c) 2017-2018 Brian Masney + */ + +#ifndef __TSL2772_H +#define __TSL2772_H + +struct tsl2772_lux { + unsigned int ch0; + unsigned int ch1; +}; + +/* Max number of segments allowable in LUX table */ +#define TSL2772_MAX_LUX_TABLE_SIZE 6 +/* The default LUX tables all have 3 elements. */ +#define TSL2772_DEF_LUX_TABLE_SZ 3 +#define TSL2772_DEFAULT_TABLE_BYTES (sizeof(struct tsl2772_lux) * \ + TSL2772_DEF_LUX_TABLE_SZ) + +/* Proximity diode to use */ +#define TSL2772_DIODE0 0x01 +#define TSL2772_DIODE1 0x02 +#define TSL2772_DIODE_BOTH 0x03 + +/* LED Power */ +#define TSL2772_100_mA 0x00 +#define TSL2772_50_mA 0x01 +#define TSL2772_25_mA 0x02 +#define TSL2772_13_mA 0x03 + +/** + * struct tsl2772_settings - Settings for the tsl2772 driver + * @als_time: Integration time of the ALS channel ADCs in 2.73 ms + * increments. Total integration time is + * (256 - als_time) * 2.73. + * @als_gain: Index into the tsl2772_als_gain array. + * @als_gain_trim: Default gain trim to account for aperture effects. + * @wait_time: Time between proximity and ALS cycles in 2.73 + * periods. + * @prox_time: Integration time of the proximity ADC in 2.73 ms + * increments. Total integration time is + * (256 - prx_time) * 2.73. + * @prox_gain: Index into the tsl2772_prx_gain array. + * @als_prox_config: The value of the ALS / Proximity configuration + * register. + * @als_cal_target: Known external ALS reading for calibration. + * @als_persistence: H/W Filters, Number of 'out of limits' ALS readings. + * @als_interrupt_en: Enable/Disable ALS interrupts + * @als_thresh_low: CH0 'low' count to trigger interrupt. + * @als_thresh_high: CH0 'high' count to trigger interrupt. + * @prox_persistence: H/W Filters, Number of 'out of limits' proximity + * readings. + * @prox_interrupt_en: Enable/Disable proximity interrupts. + * @prox_thres_low: Low threshold proximity detection. + * @prox_thres_high: High threshold proximity detection. + * @prox_pulse_count: Number if proximity emitter pulses. + * @prox_max_samples_cal: The number of samples that are taken when performing + * a proximity calibration. + * @prox_diode Which diode(s) to use for driving the external + * LED(s) for proximity sensing. + * @prox_power The amount of power to use for the external LED(s). + */ +struct tsl2772_settings { + int als_time; + int als_gain; + int als_gain_trim; + int wait_time; + int prox_time; + int prox_gain; + int als_prox_config; + int als_cal_target; + u8 als_persistence; + bool als_interrupt_en; + int als_thresh_low; + int als_thresh_high; + u8 prox_persistence; + bool prox_interrupt_en; + int prox_thres_low; + int prox_thres_high; + int prox_pulse_count; + int prox_max_samples_cal; + int prox_diode; + int prox_power; +}; + +/** + * struct tsl2772_platform_data - Platform callback, glass and defaults + * @platform_lux_table: Device specific glass coefficents + * @platform_default_settings: Device specific power on defaults + */ +struct tsl2772_platform_data { + struct tsl2772_lux platform_lux_table[TSL2772_MAX_LUX_TABLE_SIZE]; + struct tsl2772_settings *platform_default_settings; +}; + +#endif /* __TSL2772_H */ -- cgit From 98004a78bb6cf18c260cecb49cb01e36cf6a72be Mon Sep 17 00:00:00 2001 From: Vadim Pasternak Date: Tue, 27 Mar 2018 10:02:01 +0000 Subject: platform_data/mlxreg: Document fixes for hotplug device Remove redunadant description of label in struct mlxreg_hotplug_device. Change location of access_mode in struct mlxreg_hotplug_device. Signed-off-by: Vadim Pasternak Signed-off-by: Darren Hart (VMware) --- include/linux/platform_data/mlxreg.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/platform_data/mlxreg.h b/include/linux/platform_data/mlxreg.h index 2744cff1b297..19f5cb618c55 100644 --- a/include/linux/platform_data/mlxreg.h +++ b/include/linux/platform_data/mlxreg.h @@ -58,11 +58,10 @@ struct mlxreg_hotplug_device { * struct mlxreg_core_data - attributes control data: * * @label: attribute label; - * @label: attribute register offset; * @reg: attribute register; * @mask: attribute access mask; - * @mode: access mode; * @bit: attribute effective bit; + * @mode: access mode; * @np - pointer to node platform associated with attribute; * @hpdev - hotplug device data; * @health_cntr: dynamic device health indication counter; -- cgit From e7deb3c7741eaa558458696e55f57141886fcc5c Mon Sep 17 00:00:00 2001 From: Bartlomiej Zolnierkiewicz Date: Mon, 14 May 2018 15:47:30 +0200 Subject: drm: shmobile: remove unused MERAM support Since commit a521422ea4ae ("ARM: shmobile: mackerel: Remove Legacy C board code") MERAM functionality is unused. Remove it. Reviewed-by: Simon Horman Cc: Geert Uytterhoeven Acked-by: Laurent Pinchart Acked-by: Daniel Vetter Signed-off-by: Bartlomiej Zolnierkiewicz --- drivers/gpu/drm/shmobile/Kconfig | 1 - drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 42 ------------------------------ drivers/gpu/drm/shmobile/shmob_drm_crtc.h | 1 - drivers/gpu/drm/shmobile/shmob_drm_drv.h | 2 -- drivers/gpu/drm/shmobile/shmob_drm_kms.c | 11 -------- drivers/gpu/drm/shmobile/shmob_drm_kms.h | 1 - drivers/gpu/drm/shmobile/shmob_drm_plane.c | 2 -- include/linux/platform_data/shmob_drm.h | 4 --- 8 files changed, 64 deletions(-) (limited to 'include/linux') diff --git a/drivers/gpu/drm/shmobile/Kconfig b/drivers/gpu/drm/shmobile/Kconfig index c987c826daa3..0426d66660d1 100644 --- a/drivers/gpu/drm/shmobile/Kconfig +++ b/drivers/gpu/drm/shmobile/Kconfig @@ -2,7 +2,6 @@ config DRM_SHMOBILE tristate "DRM Support for SH Mobile" depends on DRM && ARM depends on ARCH_SHMOBILE || COMPILE_TEST - depends on FB_SH_MOBILE_MERAM || !FB_SH_MOBILE_MERAM select BACKLIGHT_CLASS_DEVICE select BACKLIGHT_LCD_SUPPORT select DRM_KMS_HELPER diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c index e7738939a86d..40df8887fc17 100644 --- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c +++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c @@ -21,8 +21,6 @@ #include #include -#include