[PATCH v2 3/3] media: af9035: Remove duplicate error reporting for dvbsky_usb_generic_rw
All error cases inside the function already report errors via dev_err(), and dvb_usb_v2_generic_rw also reports all error cases, so there is no silent code path when an error has occured. Signed-off-by: Stefan Brüns --- drivers/media/usb/dvb-usb-v2/af9035.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/af9035.c b/drivers/media/usb/dvb-usb-v2/af9035.c index 1b7f1af399fb..15643e2f9395 100644 --- a/drivers/media/usb/dvb-usb-v2/af9035.c +++ b/drivers/media/usb/dvb-usb-v2/af9035.c @@ -120,8 +120,6 @@ static int af9035_ctrl_msg(struct dvb_usb_device *d, struct usb_req *req) memcpy(req->rbuf, &state->buf[ACK_HDR_LEN], req->rlen); exit: mutex_unlock(&d->usb_mutex); - if (ret < 0) - dev_dbg(&intf->dev, "failed=%d\n", ret); return ret; } -- 2.21.0
[PATCH v2 2/3] media: dvbsky: Remove duplicate error reporting for dvbsky_usb_generic_rw
Errors are already reported by the common code in dvb_usb_v2_generic_io (which dvbsky_usb_generic_rw is a wrapper of), so there is no reason report the error again. Signed-off-by: Stefan Brüns --- drivers/media/usb/dvb-usb-v2/dvbsky.c | 11 --- 1 file changed, 11 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index ae0814dd202a..3ff9833597e5 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -100,8 +100,6 @@ static int dvbsky_gpio_ctrl(struct dvb_usb_device *d, u8 gport, u8 value) obuf[1] = gport; obuf[2] = value; ret = dvbsky_usb_generic_rw(d, obuf, 3, ibuf, 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); return ret; } @@ -139,8 +137,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], obuf[3] = msg[0].addr; ret = dvbsky_usb_generic_rw(d, obuf, 4, ibuf, msg[0].len + 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); if (!ret) memcpy(msg[0].buf, &ibuf[1], msg[0].len); } else { @@ -151,8 +147,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[3], msg[0].buf, msg[0].len); ret = dvbsky_usb_generic_rw(d, obuf, msg[0].len + 3, ibuf, 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); } } else { if ((msg[0].len > 60) || (msg[1].len > 60)) { @@ -170,9 +164,6 @@ static int dvbsky_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[], memcpy(&obuf[4], msg[0].buf, msg[0].len); ret = dvbsky_usb_generic_rw(d, obuf, msg[0].len + 4, ibuf, msg[1].len + 1); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); - if (!ret) memcpy(msg[1].buf, &ibuf[1], msg[1].len); } @@ -201,8 +192,6 @@ static int dvbsky_rc_query(struct dvb_usb_device *d) obuf[0] = 0x10; ret = dvbsky_usb_generic_rw(d, obuf, 1, ibuf, 2); - if (ret) - dev_err(&d->udev->dev, "failed=%d\n", ret); if (ret == 0) code = (ibuf[0] << 8) | ibuf[1]; if (code != 0x) { -- 2.21.0
[PATCH v2 1/3] media: dvb-usb-v2: Report error on all error paths
actual_length != wlen is the only error path which does not generate an error message. Adding an error message here allows to report a more specific error and to remove the error reporting from the call sites. Also clean up the error paths - in case of an error, the remaining code is skipped, and ret is returned. Skip setting ret and return immediately (no cleanup necessary). Signed-off-by: Stefan Brüns --- drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c | 15 ++- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c index 5bafeb6486be..5b32d159f968 100644 --- a/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c +++ b/drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c @@ -37,14 +37,19 @@ static int dvb_usb_v2_generic_io(struct dvb_usb_device *d, ret = usb_bulk_msg(d->udev, usb_sndbulkpipe(d->udev, d->props->generic_bulk_ctrl_endpoint), wbuf, wlen, &actual_length, 2000); - if (ret < 0) + if (ret) { dev_err(&d->udev->dev, "%s: usb_bulk_msg() failed=%d\n", KBUILD_MODNAME, ret); - else - ret = actual_length != wlen ? -EIO : 0; + return ret; + } + if (actual_length != wlen) { + dev_err(&d->udev->dev, "%s: usb_bulk_msg() write length=%d, actual=%d\n", + KBUILD_MODNAME, wlen, actual_length); + return -EIO; + } - /* an answer is expected, and no error before */ - if (!ret && rbuf && rlen) { + /* an answer is expected */ + if (rbuf && rlen) { if (d->props->generic_bulk_ctrl_delay) usleep_range(d->props->generic_bulk_ctrl_delay, d->props->generic_bulk_ctrl_delay -- 2.21.0
[PATCH v2 0/3] Cleanup error reporting
Almost all error path in dvb_usb_v2_generic_io report a specific error using dev_err(), only the "writelen != actuallen" case was omitted. Add an error message. Remove any extra error reporting from the call sites which was only required for this case. Only affected drivers are dvbsky and af9035. v2: Add Signed-off-by Stefan Brüns (3): media: dvb-usb-v2: Report error on all error paths media: dvbsky: Remove duplicate error reporting for dvbsky_usb_generic_rw media: af9035: Remove duplicate error reporting for dvbsky_usb_generic_rw drivers/media/usb/dvb-usb-v2/af9035.c | 2 -- drivers/media/usb/dvb-usb-v2/dvb_usb_urb.c | 15 ++- drivers/media/usb/dvb-usb-v2/dvbsky.c | 11 --- 3 files changed, 10 insertions(+), 18 deletions(-) -- 2.21.0
Re: [PATCH v1 0/2] Remove duplicate driver for MyGica T230C
On Wednesday, 10 January 2018 00:33:37 CET Stefan Brüns wrote: > In 2017-02, two drivers for the T230C where submitted, but until now > only the one based on the older dvb-usb/cxusb.c driver has been part > of the mainline kernel. As a dvb-usb-v2 driver is preferable, remove > the other driver. > > The cxusb.c patch also contained an unrelated change for the T230, > i.e. a correction of the RC model. As this change apparently is > correct, restore it. This has not been tested due to lack of hardware. > > > Evgeny Plehov (1): > Revert "[media] dvb-usb-cxusb: Geniatech T230C support" > > Stefan Brüns (1): > [media] cxusb: restore RC_MAP for MyGica T230 > > drivers/media/usb/dvb-usb/cxusb.c | 137 > ------ 1 file changed, 137 deletions(-) Ping! -- Stefan Brüns / Bergstraße 21 / 52062 Aachen home: +49 241 53809034 mobile: +49 151 50412019 signature.asc Description: This is a digitally signed message part.
Re: [PATCH][V2] iio: adc: ina2xx: add in early -EINVAL returns in case statements
On Dienstag, 16. Oktober 2018 18:14:18 CEST Colin King wrote: > From: Colin Ian King > > Static analysis with CoverityScan is throwing warnings that specific > case statements are missing breaks. Rather than adding breaks, add > return -EINVAL to the specific case statements to clarify the > error return paths. Fix also saves 50 bytes. > > Before: >text data bss dec hex filename > 21418 4936 128 264826772 drivers/iio/adc/ina2xx-adc.o > > After: > dec hex filename > 21370 4936 128 264346742 drivers/iio/adc/ina2xx-adc.o > > (gcc 8.2, x86-64) > > Detected by CoverityScan, CID#1462408 ("Missing break in switch") > > --- > > V2: use returns instead of break statements to keep with the > current style used in the switch statement. > > Signed-off-by: Colin Ian King Reviewed-by: Stefan Brüns > --- > drivers/iio/adc/ina2xx-adc.c | 2 ++ > 1 file changed, 2 insertions(+) > > diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c > index d1239624187d..bdd7cba6f6b0 100644 > --- a/drivers/iio/adc/ina2xx-adc.c > +++ b/drivers/iio/adc/ina2xx-adc.c > @@ -250,6 +250,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, > *val2 = chip->shunt_resistor_uohm; > return IIO_VAL_FRACTIONAL; > } > + return -EINVAL; > > case IIO_CHAN_INFO_HARDWAREGAIN: > switch (chan->address) { > @@ -262,6 +263,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, > *val = chip->range_vbus == 32 ? 1 : 2; > return IIO_VAL_INT; > } > + return -EINVAL; > } > > return -EINVAL; -- Stefan Brüns / Bergstraße 21 / 52062 Aachen home: +49 241 53809034 mobile: +49 151 50412019 signature.asc Description: This is a digitally signed message part.
[PATCH] media: dvbsky: Avoid leaking dvb frontend
Commit 14f4eaeddabc ("media: dvbsky: fix driver unregister logic") fixed a use-after-free by removing the reference to the frontend after deleting the backing i2c device. This has the unfortunate side effect the frontend device is never freed in the dvb core leaving a dangling device, leading to errors when the dvb core tries to register the frontend after e.g. a replug as reported here: https://www.spinics.net/lists/linux-media/msg138181.html media: dvbsky: issues with DVBSky T680CI === [ 561.119145] sp2 8-0040: CIMaX SP2 successfully attached [ 561.119161] usb 2-3: DVB: registering adapter 0 frontend 0 (Silicon Labs Si2168)... [ 561.119174] sysfs: cannot create duplicate filename '/class/dvb/ dvb0.frontend0' === The use after free happened as dvb_usbv2_disconnect calls in this order: - dvb_usb_device::props->exit(...) - dvb_usbv2_adapter_frontend_exit(...) + if (fe) dvb_unregister_frontend(fe) + dvb_usb_device::props->frontend_detach(...) Moving the release of the i2c device from exit() to frontend_detach() avoids the dangling pointer access and allows the core to unregister the frontend. This was originally reported for a DVBSky T680CI, but it also affects the MyGica T230C. As all supported devices structure the registration/ unregistration identically, apply the change for all device types. Signed-off-by: Stefan Brüns --- drivers/media/usb/dvb-usb-v2/dvbsky.c | 18 ++ 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index dffcadd8c834..3ff9833597e5 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -604,16 +604,18 @@ static int dvbsky_init(struct dvb_usb_device *d) return 0; } -static void dvbsky_exit(struct dvb_usb_device *d) +static int dvbsky_frontend_detach(struct dvb_usb_adapter *adap) { + struct dvb_usb_device *d = adap_to_d(adap); struct dvbsky_state *state = d_to_priv(d); - struct dvb_usb_adapter *adap = &d->adapter[0]; + + dev_dbg(&d->udev->dev, "%s: adap=%d\n", __func__, adap->id); dvb_module_release(state->i2c_client_tuner); dvb_module_release(state->i2c_client_demod); dvb_module_release(state->i2c_client_ci); - adap->fe[0] = NULL; + return 0; } /* DVB USB Driver stuff */ @@ -629,11 +631,11 @@ static struct dvb_usb_device_properties dvbsky_s960_props = { .i2c_algo = &dvbsky_i2c_algo, .frontend_attach = dvbsky_s960_attach, + .frontend_detach = dvbsky_frontend_detach, .init = dvbsky_init, .get_rc_config= dvbsky_get_rc_config, .streaming_ctrl = dvbsky_streaming_ctrl, .identify_state = dvbsky_identify_state, - .exit = dvbsky_exit, .read_mac_address = dvbsky_read_mac_addr, .num_adapters = 1, @@ -656,11 +658,11 @@ static struct dvb_usb_device_properties dvbsky_s960c_props = { .i2c_algo = &dvbsky_i2c_algo, .frontend_attach = dvbsky_s960c_attach, + .frontend_detach = dvbsky_frontend_detach, .init = dvbsky_init, .get_rc_config= dvbsky_get_rc_config, .streaming_ctrl = dvbsky_streaming_ctrl, .identify_state = dvbsky_identify_state, - .exit = dvbsky_exit, .read_mac_address = dvbsky_read_mac_addr, .num_adapters = 1, @@ -683,11 +685,11 @@ static struct dvb_usb_device_properties dvbsky_t680c_props = { .i2c_algo = &dvbsky_i2c_algo, .frontend_attach = dvbsky_t680c_attach, + .frontend_detach = dvbsky_frontend_detach, .init = dvbsky_init, .get_rc_config= dvbsky_get_rc_config, .streaming_ctrl = dvbsky_streaming_ctrl, .identify_state = dvbsky_identify_state, - .exit = dvbsky_exit, .read_mac_address = dvbsky_read_mac_addr, .num_adapters = 1, @@ -710,11 +712,11 @@ static struct dvb_usb_device_properties dvbsky_t330_props = { .i2c_algo = &dvbsky_i2c_algo, .frontend_attach = dvbsky_t330_attach, + .frontend_detach = dvbsky_frontend_detach, .init = dvbsky_init, .get_rc_config= dvbsky_get_rc_config, .streaming_ctrl = dvbsky_streaming_ctrl, .identify_state = dvbsky_identify_state, - .exit = dvbsky_exit, .read_mac_address = dvbsky_read_mac_addr, .num_adapters = 1, @@ -737,11 +739,11 @@ static struct dvb_usb_device_properties mygica_t230c_props = { .i2c_algo = &dvbsky_i2c_algo, .frontend_attach = dvbsky_mygica_t230c_attach, + .frontend_detach = dvbsky_frontend_detach, .init = dvbsky_init, .get_rc_config= dvbsky_get_rc_config, .str
Re: [PATCH] iio: adc: ina2xx: fix missing break statement
On Montag, 8. Oktober 2018 23:09:04 CEST Colin King wrote: > From: Colin Ian King > > The IIO_CHAN_INFO_SCALE case is missing a break statement and in > the unlikely event that chan->address is not matched in the nested > switch statement then the code falls through to the following > IIO_CHAN_INFO_HARDWAREGAIN case. Fix this by adding the missing > break. While we are fixing this, it's probably a good idea to > add in a break statement to the IIO_CHAN_INFO_HARDWAREGAIN case > too (this is a moot point). > > Detected by CoverityScan, CID#1462408 ("Missing break in switch") Although it is good for code clarity to add a break statement, the code can never return anything but -EINVAL in case chan->address is not handled in IIO_CHAN_INFO_SCALE: - switch (mask) { case IIO_CHAN_INFO_SCALE: switch (chan->address) { case INA2XX_SHUNT_VOLTAGE: ... return IIO_VAL_FRACTIONAL; case INA2XX_BUS_VOLTAGE: ... return IIO_VAL_FRACTIONAL; case INA2XX_CURRENT: ... return IIO_VAL_FRACTIONAL; case INA2XX_POWER: ... return IIO_VAL_FRACTIONAL; } case IIO_CHAN_INFO_HARDWAREGAIN: switch (chan->address) { case INA2XX_SHUNT_VOLTAGE: ... return IIO_VAL_FRACTIONAL; case INA2XX_BUS_VOLTAGE: ... return IIO_VAL_INT; } } return -EINVAL; - The addresses handled in INFO_HARDWAREGAIN is a subset of the ones in INFO_SCALE. I would prefer an early "return -EINVAL" here, as it matches better with the other "switch (mask)" cases above. Kind regards, Stefan -- Stefan Brüns / Bergstraße 21 / 52062 Aachen home: +49 241 53809034 mobile: +49 151 50412019 signature.asc Description: This is a digitally signed message part.
[PATCH 0/2] Enhancements for TI INA219/220 current/voltage monitors
The ina2xx adc driver does not handle differences between INA219/220 and INA226/230/231 completely: - The 219 uses a 4 bit register field for for the bus/shunt voltage adc integration times each, whereas the 226 uses two 3 bit fields for the integration times and an additional 3 bit averaging field. The first patch correct setting integration times in the config register. - The 219 also has an adjustable PGA. The default setting allows the maximum range, but also reduces accuracy. The second patch adds attributes for shunt PGA gain and bus voltage range (bus voltage PGA), and sets the config register accordingly. Stefan Brüns (2): iio: adc: Fix integration time/averaging for INA219/220 iio: adc: Allow setting Shunt Voltage PGA gain and Bus Voltage range drivers/iio/adc/ina2xx-adc.c | 344 --- 1 file changed, 322 insertions(+), 22 deletions(-) -- 2.12.0
[PATCH 2/2] iio: adc: Allow setting Shunt Voltage PGA gain and Bus Voltage range
Reducing shunt and bus voltage range improves the accuracy, so allow altering the default settings. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 165 ++- 1 file changed, 164 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index d1678f886297..856409ecceb3 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -47,8 +47,10 @@ #define INA2XX_MAX_REGISTERS8 /* settings - depend on use case */ -#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */ #define INA219_DEFAULT_IT 532 +#define INA219_DEFAULT_BRNG 32 +#define INA219_DEFAULT_PGA 125 /* 1000/8 */ #define INA226_CONFIG_DEFAULT 0x4327 #define INA226_DEFAULT_AVG 4 #define INA226_DEFAULT_IT 1110 @@ -61,6 +63,14 @@ */ #define INA2XX_MODE_MASK GENMASK(3, 0) +/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */ +#define INA219_PGA_MASKGENMASK(12, 11) +#define INA219_SHIFT_PGA(val) ((val) << 11) + +/* VBus range: 32V (default), 16V */ +#define INA219_BRNG_MASK BIT(13) +#define INA219_SHIFT_BRNG(val) ((val) << 13) + /* Averaging for VBus/VShunt/Power */ #define INA226_AVG_MASKGENMASK(11, 9) #define INA226_SHIFT_AVG(val) ((val) << 9) @@ -125,6 +135,8 @@ struct ina2xx_chip_info { int avg; int int_time_vbus; /* Bus voltage integration time uS */ int int_time_vshunt; /* Shunt voltage integration time uS */ + int range_vbus; /* Bus voltage maximum in V */ + int pga_gain_vshunt; /* Shunt voltage PGA gain */ bool allow_async_readout; }; @@ -351,6 +363,44 @@ static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, return 0; } +static int ina219_set_vbus_range(struct ina2xx_chip_info *chip, +unsigned int range, +unsigned int *config) +{ + if (range != 16 && range != 32) + return -EINVAL; + + chip->range_vbus = range; + + *config &= ~INA219_BRNG_MASK; + *config |= INA219_SHIFT_BRNG(range == 32 ? 1 : 0) & INA219_BRNG_MASK; + + return 0; +} + +static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 }; + +static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip, + unsigned int gain, + unsigned int *config) +{ + int bits; + + if (gain < 125 || gain > 1000) + return -EINVAL; + + bits = find_closest(gain, ina219_vshunt_gain_tab, + ARRAY_SIZE(ina219_vshunt_gain_tab)); + + chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits]; + bits = 3 - bits; + + *config &= ~INA219_PGA_MASK; + *config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK; + + return 0; +} + static int ina2xx_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -485,6 +535,92 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, return len; } +static ssize_t ina219_bus_voltage_range_show(struct device *dev, +struct device_attribute *attr, +char *buf) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + + return sprintf(buf, "%d\n", chip->range_vbus); +} + +static ssize_t ina219_bus_voltage_range_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + unsigned long val; + unsigned int config, tmp; + int ret; + + ret = kstrtoul((const char *) buf, 10, &val); + if (ret) + return ret; + + mutex_lock(&chip->state_lock); + + ret = regmap_read(chip->regmap, INA2XX_CONFIG, &config); + if (ret) + goto err; + + tmp = config; + + ret = ina219_set_vbus_range(chip, val, &config); + + if (!ret && (tmp != config)) + ret = regmap_write(chip->regmap, INA2XX_CONFIG, config); + + if (!ret) + ret = len; +err: + mutex_unlock(&chip->state_lock); + + return ret; +} + +static ssize_t ina219_shunt_voltage_gain_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + int va
[PATCH 1/2] iio: adc: Fix integration time/averaging for INA219/220
INA226/230/231 has integration times per voltage channel and common averaging setting for both channels, while the INA219/220 only has a combined integration time/averaging setting per channel. Only expose the averaging attribute for the INA226, and expose the correct integration times for the INA219. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 179 ++- 1 file changed, 158 insertions(+), 21 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 3263231276ca..d1678f886297 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -48,6 +48,7 @@ /* settings - depend on use case */ #define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA219_DEFAULT_IT 532 #define INA226_CONFIG_DEFAULT 0x4327 #define INA226_DEFAULT_AVG 4 #define INA226_DEFAULT_IT 1110 @@ -55,19 +56,24 @@ #define INA2XX_RSHUNT_DEFAULT 1 /* - * bit mask for reading the averaging setting in the configuration register + * bit masks for reading the settings in the configuration register * FIXME: use regmap_fields. */ #define INA2XX_MODE_MASK GENMASK(3, 0) +/* Averaging for VBus/VShunt/Power */ #define INA226_AVG_MASKGENMASK(11, 9) #define INA226_SHIFT_AVG(val) ((val) << 9) /* Integration time for VBus */ +#define INA219_ITB_MASKGENMASK(10, 7) +#define INA219_SHIFT_ITB(val) ((val) << 7) #define INA226_ITB_MASKGENMASK(8, 6) #define INA226_SHIFT_ITB(val) ((val) << 6) /* Integration time for VShunt */ +#define INA219_ITS_MASKGENMASK(6, 3) +#define INA219_SHIFT_ITS(val) ((val) << 3) #define INA226_ITS_MASKGENMASK(5, 3) #define INA226_SHIFT_ITS(val) ((val) << 3) @@ -107,6 +113,7 @@ struct ina2xx_config { int bus_voltage_shift; int bus_voltage_lsb;/* uV */ int power_lsb; /* uW */ + enum ina2xx_ids chip_id; }; struct ina2xx_chip_info { @@ -129,6 +136,7 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_shift = 3, .bus_voltage_lsb = 4000, .power_lsb = 2, + .chip_id = ina219, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, @@ -137,6 +145,7 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb = 25000, + .chip_id = ina226, }, }; @@ -282,6 +291,66 @@ static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip, return 0; } +/* Conversion times in uS. */ +static const int ina219_conv_time_tab_subsample[] = { 84, 148, 276, 532 }; +static const int ina219_conv_time_tab_average[] = { 532, 1060, 2130, 4260, + 8510, 17020, 34050, 68100}; + +static int ina219_lookup_int_time(unsigned int *val_us, int *bits) +{ + if (*val_us > 68100 || *val_us < 84) + return -EINVAL; + + if (*val_us <= 532) { + *bits = find_closest(*val_us, ina219_conv_time_tab_subsample, + ARRAY_SIZE(ina219_conv_time_tab_subsample)); + *val_us = ina219_conv_time_tab_subsample[*bits]; + } else { + *bits = find_closest(*val_us, ina219_conv_time_tab_average, + ARRAY_SIZE(ina219_conv_time_tab_average)); + *val_us = ina219_conv_time_tab_average[*bits]; + *bits |= 0x8; + } + + return 0; +} + +static int ina219_set_int_time_vbus(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vbus = val_us_best; + + *config &= ~INA219_ITB_MASK; + *config |= INA219_SHIFT_ITB(bits) & INA219_ITB_MASK; + + return 0; +} + +static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vshunt = val_us_best; + + *config &= ~INA219_ITS_MASK; + *config |= INA219_SHIFT_ITS(bits) & INA219_ITS_MASK; + + return 0; +} + static int ina2xx_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -307,10 +376,15 @@
[PATCH 0/2] iio: Trivial fixes for INA219, Documentation
The following two patches fix some trivial issues, no functional changes: Stefan Brüns (2): iio: Documentation: Remove (partially) duplicate line iio: adc: Fix bad GENMASK use, typos, whitespace Documentation/ABI/testing/sysfs-bus-iio-meas-spec | 1 - drivers/iio/adc/ina2xx-adc.c | 12 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) -- 2.12.2
[PATCH 1/2] iio: Documentation: Remove (partially) duplicate line
Signed-off-by: Stefan Brüns --- Documentation/ABI/testing/sysfs-bus-iio-meas-spec | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio-meas-spec b/Documentation/ABI/testing/sysfs-bus-iio-meas-spec index 1a6265e92e2f..6d47e548eee5 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-meas-spec +++ b/Documentation/ABI/testing/sysfs-bus-iio-meas-spec @@ -5,4 +5,3 @@ Description: Reading returns either '1' or '0'. '1' means that the battery level supplied to sensor is below 2.25V. This ABI is available for tsys02d, htu21, ms8607 - This ABI is available for htu21, ms8607 -- 2.12.2
[PATCH 2/2] iio: adc: Fix bad GENMASK use, typos, whitespace
Commit 7906dd52c5a0 ("iio: ina2xx: Fix whitespace and re-order code") changed the register number of the MASK_ENABLE register from 0x06 to the value equivalent GENMASK(2,1), although its no mask. Also fix a typo (INA2_6_6 instead of INA2_2_6), and use the datasheet name ("Mask/Enable") for the register number define. Fix bad indentation for channel attributes. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 12 ++-- 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 3263231276ca..f395101d68d7 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -41,8 +41,8 @@ #define INA2XX_CURRENT 0x04 /* readonly */ #define INA2XX_CALIBRATION 0x05 -#define INA226_ALERT_MASK GENMASK(2, 1) -#define INA266_CVRFBIT(3) +#define INA226_MASK_ENABLE 0x06 +#define INA226_CVRFBIT(3) #define INA2XX_MAX_REGISTERS8 @@ -416,8 +416,8 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, .address = (_address), \ .indexed = 1, \ .channel = (_index), \ - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ - | BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = (_index), \ @@ -480,12 +480,12 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) */ if (!chip->allow_async_readout) do { - ret = regmap_read(chip->regmap, INA226_ALERT_MASK, + ret = regmap_read(chip->regmap, INA226_MASK_ENABLE, &alert); if (ret < 0) return ret; - alert &= INA266_CVRF; + alert &= INA226_CVRF; } while (!alert); /* -- 2.12.2
[PATCH v3 1/3] [media] si2157: Add support for Si2141-A10
The Si2141 needs two distinct commands for powerup/reset, otherwise it will not respond to chip revision requests. It also needs a firmware to run properly. Signed-off-by: Stefan Brüns --- drivers/media/tuners/si2157.c | 23 +-- drivers/media/tuners/si2157_priv.h | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 57b250847cd3..e35b1faf0ddc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -106,6 +106,9 @@ static int si2157_init(struct dvb_frontend *fe) if (dev->chiptype == SI2157_CHIPTYPE_SI2146) { memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); cmd.wlen = 9; + } else if (dev->chiptype == SI2157_CHIPTYPE_SI2141) { + memcpy(cmd.args, "\xc0\x00\x0d\x0e\x00\x01\x01\x01\x01\x03", 10); + cmd.wlen = 10; } else { memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); cmd.wlen = 15; @@ -115,6 +118,15 @@ static int si2157_init(struct dvb_frontend *fe) if (ret) goto err; + /* Si2141 needs a second command before it answers the revision query */ + if (dev->chiptype == SI2157_CHIPTYPE_SI2141) { + memcpy(cmd.args, "\xc0\x08\x01\x02\x00\x00\x01", 7); + cmd.wlen = 7; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + } + /* query chip revision */ memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; @@ -131,12 +143,16 @@ static int si2157_init(struct dvb_frontend *fe) #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0) #define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0) + #define SI2141_A10 ('A' << 24 | 41 << 16 | '1' << 8 | '0' << 0) switch (chip_id) { case SI2158_A20: case SI2148_A20: fw_name = SI2158_A20_FIRMWARE; break; + case SI2141_A10: + fw_name = SI2141_A10_FIRMWARE; + break; case SI2157_A30: case SI2147_A30: case SI2146_A10: @@ -371,7 +387,7 @@ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops si2157_ops = { .info = { - .name = "Silicon Labs Si2146/2147/2148/2157/2158", + .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", .frequency_min = 4200, .frequency_max = 87000, }, @@ -471,6 +487,7 @@ static int si2157_probe(struct i2c_client *client, #endif dev_info(&client->dev, "Silicon Labs %s successfully attached\n", + dev->chiptype == SI2157_CHIPTYPE_SI2141 ? "Si2141" : dev->chiptype == SI2157_CHIPTYPE_SI2146 ? "Si2146" : "Si2147/2148/2157/2158"); @@ -508,6 +525,7 @@ static int si2157_remove(struct i2c_client *client) static const struct i2c_device_id si2157_id_table[] = { {"si2157", SI2157_CHIPTYPE_SI2157}, {"si2146", SI2157_CHIPTYPE_SI2146}, + {"si2141", SI2157_CHIPTYPE_SI2141}, {} }; MODULE_DEVICE_TABLE(i2c, si2157_id_table); @@ -524,7 +542,8 @@ static struct i2c_driver si2157_driver = { module_i2c_driver(si2157_driver); -MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver"); +MODULE_DESCRIPTION("Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SI2158_A20_FIRMWARE); +MODULE_FIRMWARE(SI2141_A10_FIRMWARE); diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index d6b2c7b44053..e6436f74abaa 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -42,6 +42,7 @@ struct si2157_dev { #define SI2157_CHIPTYPE_SI2157 0 #define SI2157_CHIPTYPE_SI2146 1 +#define SI2157_CHIPTYPE_SI2141 2 /* firmware command struct */ #define SI2157_ARGLEN 30 @@ -52,5 +53,6 @@ struct si2157_cmd { }; #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" +#define SI2141_A10_FIRMWARE "dvb-tuner-si2141-a10-01.fw" #endif -- 2.11.0
[PATCH v3 2/3] [media] si2168: add support for Si2168-D60
Add handling for new revision, requiring new firmware. Signed-off-by: Stefan Brüns --- drivers/media/dvb-frontends/si2168.c | 4 drivers/media/dvb-frontends/si2168_priv.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 20b4a659e2e4..28f3bbe0af25 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -674,6 +674,9 @@ static int si2168_probe(struct i2c_client *client, case SI2168_CHIP_ID_B40: dev->firmware_name = SI2168_B40_FIRMWARE; break; + case SI2168_CHIP_ID_D60: + dev->firmware_name = SI2168_D60_FIRMWARE; + break; default: dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n", cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); @@ -761,3 +764,4 @@ MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SI2168_A20_FIRMWARE); MODULE_FIRMWARE(SI2168_A30_FIRMWARE); MODULE_FIRMWARE(SI2168_B40_FIRMWARE); +MODULE_FIRMWARE(SI2168_D60_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 7843ccb448a0..4baa95b7d648 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -25,6 +25,7 @@ #define SI2168_A20_FIRMWARE "dvb-demod-si2168-a20-01.fw" #define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw" #define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw" +#define SI2168_D60_FIRMWARE "dvb-demod-si2168-d60-01.fw" #define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-02.fw" /* state struct */ @@ -37,6 +38,7 @@ struct si2168_dev { #define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) #define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) #define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) + #define SI2168_CHIP_ID_D60 ('D' << 24 | 68 << 16 | '6' << 8 | '0' << 0) unsigned int chip_id; unsigned int version; const char *firmware_name; -- 2.11.0
[PATCH v3 3/3] [media] dvbsky: MyGica T230C support
Mygica T230 DVB-T/T2/C USB stick support. It uses the same FX2/Si2168 bridge/demodulator combo as the other devices supported by the driver, but uses the Si2141 tuner. Several DVB-T (MPEG2) and DVB-T2 (H.265) channels were tested, as well as the included remote control. Signed-off-by: Stefan Brüns --- v2: add support to the dvbsky driver instead of cxusb, correct RC model v3: fixed coding style issues, use name as printed on box for id table --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb-v2/dvbsky.c | 88 +++ 2 files changed, 89 insertions(+) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index a7a4674ccc40..ce4a3d574dd7 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -380,6 +380,7 @@ #define USB_PID_SONY_PLAYTV0x0003 #define USB_PID_MYGICA_D6890xd811 #define USB_PID_MYGICA_T2300xc688 +#define USB_PID_MYGICA_T230C 0xc689 #define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 #define USB_PID_ELGATO_EYETV_DTT 0x0021 #define USB_PID_ELGATO_EYETV_DTT_2 0x003f diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 02dbc6c45423..f98225767712 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -665,6 +665,65 @@ static int dvbsky_t330_attach(struct dvb_usb_adapter *adap) return ret; } +static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap) +{ + struct dvbsky_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + struct i2c_adapter *i2c_adapter; + struct i2c_client *client_demod, *client_tuner; + struct i2c_board_info info; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + + /* attach demod */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &i2c_adapter; + si2168_config.fe = &adap->fe[0]; + si2168_config.ts_mode = SI2168_TS_PARALLEL; + si2168_config.ts_clock_inv = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", sizeof(info.type)); + info.addr = 0x64; + info.platform_data = &si2168_config; + + request_module("si2168"); + client_demod = i2c_new_device(&d->i2c_adap, &info); + if (!client_demod || !client_demod->dev.driver) + goto fail_demod_device; + if (!try_module_get(client_demod->dev.driver->owner)) + goto fail_demod_module; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = adap->fe[0]; + si2157_config.if_port = 0; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2141", sizeof(info.type)); + info.addr = 0x60; + info.platform_data = &si2157_config; + + request_module("si2157"); + client_tuner = i2c_new_device(i2c_adapter, &info); + if (!client_tuner || !client_tuner->dev.driver) + goto fail_tuner_device; + if (!try_module_get(client_tuner->dev.driver->owner)) + goto fail_tuner_module; + + state->i2c_client_demod = client_demod; + state->i2c_client_tuner = client_tuner; + return 0; + +fail_tuner_module: + i2c_unregister_device(client_tuner); +fail_tuner_device: + module_put(client_demod->dev.driver->owner); +fail_demod_module: + i2c_unregister_device(client_demod); +fail_demod_device: + return -ENODEV; +} + + static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name) { dvbsky_gpio_ctrl(d, 0x04, 1); @@ -830,6 +889,32 @@ static struct dvb_usb_device_properties dvbsky_t330_props = { } }; +static struct dvb_usb_device_properties mygica_t230c_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct dvbsky_state), + + .generic_bulk_ctrl_endpoint = 0x01, + .generic_bulk_ctrl_endpoint_response = 0x81, + .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY, + + .i2c_algo = &dvbsky_i2c_algo, + .frontend_attach = dvbsky_mygica_t230c_attach, + .init = dvbsky_init, + .get_rc_config= dvbsky_get_rc_config, + .streaming_ctrl = dvbsky_streaming_ctrl, + .identify_state = dvbsky_identify_state, + .exit = dvbsky_exit, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB_USB_STREAM_BULK(0x82, 8, 4096), +
[PATCH v3 0/3] Add support for MyGica T230C DVB-T2 stick
The required command sequence for the new tuner (Si2141) was traced from the current Windows driver and verified with a small python script/libusb. The changes to the Si2168 and dvbsky driver are mostly additions of the required IDs and some glue code. Stefan Brüns (3): [media] si2157: Add support for Si2141-A10 [media] si2168: add support for Si2168-D60 [media] dvbsky: MyGica T230C support drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/dvb-frontends/si2168.c | 4 ++ drivers/media/dvb-frontends/si2168_priv.h | 2 + drivers/media/tuners/si2157.c | 23 +++- drivers/media/tuners/si2157_priv.h| 2 + drivers/media/usb/dvb-usb-v2/dvbsky.c | 88 +++ 6 files changed, 118 insertions(+), 2 deletions(-) -- 2.11.0
[PATCH v2 0/3] Add support for MyGica T230C DVB-T2 stick
The required command sequence for the new tuner (Si2141) was traced from the current Windows driver and verified with a small python script/libusb. The changes to the Si2168 and dvbsky driver are mostly additions of the required IDs and some glue code. Stefan Brüns (3): [media] si2157: Add support for Si2141-A10 [media] si2168: add support for Si2168-D60 [media] dvbsky: MyGica T230C support drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/dvb-frontends/si2168.c | 4 ++ drivers/media/dvb-frontends/si2168_priv.h | 2 + drivers/media/tuners/si2157.c | 23 +++- drivers/media/tuners/si2157_priv.h| 2 + drivers/media/usb/dvb-usb-v2/dvbsky.c | 91 +++ 6 files changed, 121 insertions(+), 2 deletions(-) -- 2.11.0
[PATCH v2 3/3] [media] dvbsky: MyGica T230C support
Mygica T230 DVB-T/T2/C USB stick support. It uses the same FX2/Si2168 bridge/demodulator combo as the other devices supported by the driver, but uses the Si2141 tuner. Several DVB-T (MPEG2) and DVB-T2 (H.265) channels were tested, as well as the include remote control. Signed-off-by: Stefan Brüns --- v2: add support to the dvbsky driver instead of cxusb, correct RC model --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb-v2/dvbsky.c | 91 +++ 2 files changed, 92 insertions(+) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index a7a4674ccc40..ce4a3d574dd7 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -380,6 +380,7 @@ #define USB_PID_SONY_PLAYTV0x0003 #define USB_PID_MYGICA_D6890xd811 #define USB_PID_MYGICA_T2300xc688 +#define USB_PID_MYGICA_T230C 0xc689 #define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 #define USB_PID_ELGATO_EYETV_DTT 0x0021 #define USB_PID_ELGATO_EYETV_DTT_2 0x003f diff --git a/drivers/media/usb/dvb-usb-v2/dvbsky.c b/drivers/media/usb/dvb-usb-v2/dvbsky.c index 02dbc6c45423..729496e5a52e 100644 --- a/drivers/media/usb/dvb-usb-v2/dvbsky.c +++ b/drivers/media/usb/dvb-usb-v2/dvbsky.c @@ -665,6 +665,68 @@ static int dvbsky_t330_attach(struct dvb_usb_adapter *adap) return ret; } +static int dvbsky_mygica_t230c_attach(struct dvb_usb_adapter *adap) +{ + struct dvbsky_state *state = adap_to_priv(adap); + struct dvb_usb_device *d = adap_to_d(adap); + int ret = 0; + struct i2c_adapter *i2c_adapter; + struct i2c_client *client_demod, *client_tuner; + struct i2c_board_info info; + struct si2168_config si2168_config; + struct si2157_config si2157_config; + + /* attach demod */ + memset(&si2168_config, 0, sizeof(si2168_config)); + si2168_config.i2c_adapter = &i2c_adapter; + si2168_config.fe = &adap->fe[0]; + si2168_config.ts_mode = SI2168_TS_PARALLEL; + si2168_config.ts_clock_inv = 1; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2168", I2C_NAME_SIZE); + info.addr = 0x64; + info.platform_data = &si2168_config; + + request_module(info.type); + client_demod = i2c_new_device(&d->i2c_adap, &info); + if (client_demod == NULL || + client_demod->dev.driver == NULL) + goto fail_demod_device; + if (!try_module_get(client_demod->dev.driver->owner)) + goto fail_demod_module; + + /* attach tuner */ + memset(&si2157_config, 0, sizeof(si2157_config)); + si2157_config.fe = adap->fe[0]; + si2157_config.if_port = 0; + memset(&info, 0, sizeof(struct i2c_board_info)); + strlcpy(info.type, "si2141", I2C_NAME_SIZE); + info.addr = 0x60; + info.platform_data = &si2157_config; + + request_module("si2157"); + client_tuner = i2c_new_device(i2c_adapter, &info); + if (client_tuner == NULL || + client_tuner->dev.driver == NULL) + goto fail_tuner_device; + if (!try_module_get(client_tuner->dev.driver->owner)) + goto fail_tuner_module; + + state->i2c_client_demod = client_demod; + state->i2c_client_tuner = client_tuner; + return ret; +fail_tuner_module: + i2c_unregister_device(client_tuner); +fail_tuner_device: + module_put(client_demod->dev.driver->owner); +fail_demod_module: + i2c_unregister_device(client_demod); +fail_demod_device: + ret = -ENODEV; + return ret; +} + + static int dvbsky_identify_state(struct dvb_usb_device *d, const char **name) { dvbsky_gpio_ctrl(d, 0x04, 1); @@ -830,6 +892,32 @@ static struct dvb_usb_device_properties dvbsky_t330_props = { } }; +static struct dvb_usb_device_properties mygica_t230c_props = { + .driver_name = KBUILD_MODNAME, + .owner = THIS_MODULE, + .adapter_nr = adapter_nr, + .size_of_priv = sizeof(struct dvbsky_state), + + .generic_bulk_ctrl_endpoint = 0x01, + .generic_bulk_ctrl_endpoint_response = 0x81, + .generic_bulk_ctrl_delay = DVBSKY_MSG_DELAY, + + .i2c_algo = &dvbsky_i2c_algo, + .frontend_attach = dvbsky_mygica_t230c_attach, + .init = dvbsky_init, + .get_rc_config= dvbsky_get_rc_config, + .streaming_ctrl = dvbsky_streaming_ctrl, + .identify_state = dvbsky_identify_state, + .exit = dvbsky_exit, + + .num_adapters = 1, + .adapter = { + { + .stream = DVB
[PATCH v2 2/3] [media] si2168: add support for Si2168-D60
Add handling for new revision, requiring new firmware. Signed-off-by: Stefan Brüns --- drivers/media/dvb-frontends/si2168.c | 4 drivers/media/dvb-frontends/si2168_priv.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 20b4a659e2e4..28f3bbe0af25 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -674,6 +674,9 @@ static int si2168_probe(struct i2c_client *client, case SI2168_CHIP_ID_B40: dev->firmware_name = SI2168_B40_FIRMWARE; break; + case SI2168_CHIP_ID_D60: + dev->firmware_name = SI2168_D60_FIRMWARE; + break; default: dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n", cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); @@ -761,3 +764,4 @@ MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SI2168_A20_FIRMWARE); MODULE_FIRMWARE(SI2168_A30_FIRMWARE); MODULE_FIRMWARE(SI2168_B40_FIRMWARE); +MODULE_FIRMWARE(SI2168_D60_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 7843ccb448a0..4baa95b7d648 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -25,6 +25,7 @@ #define SI2168_A20_FIRMWARE "dvb-demod-si2168-a20-01.fw" #define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw" #define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw" +#define SI2168_D60_FIRMWARE "dvb-demod-si2168-d60-01.fw" #define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-02.fw" /* state struct */ @@ -37,6 +38,7 @@ struct si2168_dev { #define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) #define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) #define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) + #define SI2168_CHIP_ID_D60 ('D' << 24 | 68 << 16 | '6' << 8 | '0' << 0) unsigned int chip_id; unsigned int version; const char *firmware_name; -- 2.11.0
[PATCH v2 1/3] [media] si2157: Add support for Si2141-A10
The Si2141 needs two distinct commands for powerup/reset, otherwise it will not respond to chip revision requests. It also needs a firmware to run properly. Signed-off-by: Stefan Brüns --- drivers/media/tuners/si2157.c | 23 +-- drivers/media/tuners/si2157_priv.h | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 57b250847cd3..e35b1faf0ddc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -106,6 +106,9 @@ static int si2157_init(struct dvb_frontend *fe) if (dev->chiptype == SI2157_CHIPTYPE_SI2146) { memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); cmd.wlen = 9; + } else if (dev->chiptype == SI2157_CHIPTYPE_SI2141) { + memcpy(cmd.args, "\xc0\x00\x0d\x0e\x00\x01\x01\x01\x01\x03", 10); + cmd.wlen = 10; } else { memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); cmd.wlen = 15; @@ -115,6 +118,15 @@ static int si2157_init(struct dvb_frontend *fe) if (ret) goto err; + /* Si2141 needs a second command before it answers the revision query */ + if (dev->chiptype == SI2157_CHIPTYPE_SI2141) { + memcpy(cmd.args, "\xc0\x08\x01\x02\x00\x00\x01", 7); + cmd.wlen = 7; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + } + /* query chip revision */ memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; @@ -131,12 +143,16 @@ static int si2157_init(struct dvb_frontend *fe) #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0) #define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0) + #define SI2141_A10 ('A' << 24 | 41 << 16 | '1' << 8 | '0' << 0) switch (chip_id) { case SI2158_A20: case SI2148_A20: fw_name = SI2158_A20_FIRMWARE; break; + case SI2141_A10: + fw_name = SI2141_A10_FIRMWARE; + break; case SI2157_A30: case SI2147_A30: case SI2146_A10: @@ -371,7 +387,7 @@ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops si2157_ops = { .info = { - .name = "Silicon Labs Si2146/2147/2148/2157/2158", + .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", .frequency_min = 4200, .frequency_max = 87000, }, @@ -471,6 +487,7 @@ static int si2157_probe(struct i2c_client *client, #endif dev_info(&client->dev, "Silicon Labs %s successfully attached\n", + dev->chiptype == SI2157_CHIPTYPE_SI2141 ? "Si2141" : dev->chiptype == SI2157_CHIPTYPE_SI2146 ? "Si2146" : "Si2147/2148/2157/2158"); @@ -508,6 +525,7 @@ static int si2157_remove(struct i2c_client *client) static const struct i2c_device_id si2157_id_table[] = { {"si2157", SI2157_CHIPTYPE_SI2157}, {"si2146", SI2157_CHIPTYPE_SI2146}, + {"si2141", SI2157_CHIPTYPE_SI2141}, {} }; MODULE_DEVICE_TABLE(i2c, si2157_id_table); @@ -524,7 +542,8 @@ static struct i2c_driver si2157_driver = { module_i2c_driver(si2157_driver); -MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver"); +MODULE_DESCRIPTION("Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SI2158_A20_FIRMWARE); +MODULE_FIRMWARE(SI2141_A10_FIRMWARE); diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index d6b2c7b44053..e6436f74abaa 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -42,6 +42,7 @@ struct si2157_dev { #define SI2157_CHIPTYPE_SI2157 0 #define SI2157_CHIPTYPE_SI2146 1 +#define SI2157_CHIPTYPE_SI2141 2 /* firmware command struct */ #define SI2157_ARGLEN 30 @@ -52,5 +53,6 @@ struct si2157_cmd { }; #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" +#define SI2141_A10_FIRMWARE "dvb-tuner-si2141-a10-01.fw" #endif -- 2.11.0
[PATCH v2] [media] dvb-usb-firmware: don't do DMA on stack
The buffer allocation for the firmware data was changed in commit 43fab9793c1f ("dvb-usb: don't use stack for firmware load"), but the same applies for the reset value. Signed-off-by: Stefan Brüns --- This patch replaces the earlier submission conflicting with commit 43fab9793c1f, which was a subset of the submitted patch. drivers/media/usb/dvb-usb/dvb-usb-firmware.c | 21 - 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c index 3271b3fee1f4..1f008f354282 100644 --- a/drivers/media/usb/dvb-usb/dvb-usb-firmware.c +++ b/drivers/media/usb/dvb-usb/dvb-usb-firmware.c @@ -36,16 +36,19 @@ static int usb_cypress_writemem(struct usb_device *udev,u16 addr,u8 *data, u8 le int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw, int type) { struct hexline *hx; - u8 reset; + u8 *buf; + u8 *reset; int ret,pos=0; + u16 cpu_cs_register = cypress[type].cpu_cs_register; - hx = kmalloc(sizeof(*hx), GFP_KERNEL); - if (!hx) + buf = kmalloc(sizeof(*hx), GFP_KERNEL); + if (!buf) return -ENOMEM; + hx = (struct hexline *)buf; /* stop the CPU */ - reset = 1; - if ((ret = usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1)) != 1) + buf[0] = 1; + if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) err("could not stop the USB controller CPU."); while ((ret = dvb_usb_get_hexline(fw, hx, &pos)) > 0) { @@ -61,21 +64,21 @@ int usb_cypress_load_firmware(struct usb_device *udev, const struct firmware *fw } if (ret < 0) { err("firmware download failed at %d with %d",pos,ret); - kfree(hx); + kfree(buf); return ret; } if (ret == 0) { /* restart the CPU */ - reset = 0; - if (ret || usb_cypress_writemem(udev,cypress[type].cpu_cs_register,&reset,1) != 1) { + buf[0] = 0; + if (usb_cypress_writemem(udev, cpu_cs_register, buf, 1) != 1) { err("could not restart the USB controller CPU."); ret = -EINVAL; } } else ret = -EIO; - kfree(hx); + kfree(buf); return ret; } -- 2.11.0
[PATCH 1/3] [media] si2157: Add support for Si2141-A10
The Si2141 needs two distinct commands for powerup/reset, otherwise it will not respond to chip revision requests. It also needs a firmware to run properly. Signed-off-by: Stefan Brüns --- drivers/media/tuners/si2157.c | 23 +-- drivers/media/tuners/si2157_priv.h | 2 ++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/drivers/media/tuners/si2157.c b/drivers/media/tuners/si2157.c index 57b250847cd3..e35b1faf0ddc 100644 --- a/drivers/media/tuners/si2157.c +++ b/drivers/media/tuners/si2157.c @@ -106,6 +106,9 @@ static int si2157_init(struct dvb_frontend *fe) if (dev->chiptype == SI2157_CHIPTYPE_SI2146) { memcpy(cmd.args, "\xc0\x05\x01\x00\x00\x0b\x00\x00\x01", 9); cmd.wlen = 9; + } else if (dev->chiptype == SI2157_CHIPTYPE_SI2141) { + memcpy(cmd.args, "\xc0\x00\x0d\x0e\x00\x01\x01\x01\x01\x03", 10); + cmd.wlen = 10; } else { memcpy(cmd.args, "\xc0\x00\x0c\x00\x00\x01\x01\x01\x01\x01\x01\x02\x00\x00\x01", 15); cmd.wlen = 15; @@ -115,6 +118,15 @@ static int si2157_init(struct dvb_frontend *fe) if (ret) goto err; + /* Si2141 needs a second command before it answers the revision query */ + if (dev->chiptype == SI2157_CHIPTYPE_SI2141) { + memcpy(cmd.args, "\xc0\x08\x01\x02\x00\x00\x01", 7); + cmd.wlen = 7; + ret = si2157_cmd_execute(client, &cmd); + if (ret) + goto err; + } + /* query chip revision */ memcpy(cmd.args, "\x02", 1); cmd.wlen = 1; @@ -131,12 +143,16 @@ static int si2157_init(struct dvb_frontend *fe) #define SI2157_A30 ('A' << 24 | 57 << 16 | '3' << 8 | '0' << 0) #define SI2147_A30 ('A' << 24 | 47 << 16 | '3' << 8 | '0' << 0) #define SI2146_A10 ('A' << 24 | 46 << 16 | '1' << 8 | '0' << 0) + #define SI2141_A10 ('A' << 24 | 41 << 16 | '1' << 8 | '0' << 0) switch (chip_id) { case SI2158_A20: case SI2148_A20: fw_name = SI2158_A20_FIRMWARE; break; + case SI2141_A10: + fw_name = SI2141_A10_FIRMWARE; + break; case SI2157_A30: case SI2147_A30: case SI2146_A10: @@ -371,7 +387,7 @@ static int si2157_get_if_frequency(struct dvb_frontend *fe, u32 *frequency) static const struct dvb_tuner_ops si2157_ops = { .info = { - .name = "Silicon Labs Si2146/2147/2148/2157/2158", + .name = "Silicon Labs Si2141/Si2146/2147/2148/2157/2158", .frequency_min = 4200, .frequency_max = 87000, }, @@ -471,6 +487,7 @@ static int si2157_probe(struct i2c_client *client, #endif dev_info(&client->dev, "Silicon Labs %s successfully attached\n", + dev->chiptype == SI2157_CHIPTYPE_SI2141 ? "Si2141" : dev->chiptype == SI2157_CHIPTYPE_SI2146 ? "Si2146" : "Si2147/2148/2157/2158"); @@ -508,6 +525,7 @@ static int si2157_remove(struct i2c_client *client) static const struct i2c_device_id si2157_id_table[] = { {"si2157", SI2157_CHIPTYPE_SI2157}, {"si2146", SI2157_CHIPTYPE_SI2146}, + {"si2141", SI2157_CHIPTYPE_SI2141}, {} }; MODULE_DEVICE_TABLE(i2c, si2157_id_table); @@ -524,7 +542,8 @@ static struct i2c_driver si2157_driver = { module_i2c_driver(si2157_driver); -MODULE_DESCRIPTION("Silicon Labs Si2146/2147/2148/2157/2158 silicon tuner driver"); +MODULE_DESCRIPTION("Silicon Labs Si2141/Si2146/2147/2148/2157/2158 silicon tuner driver"); MODULE_AUTHOR("Antti Palosaari "); MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SI2158_A20_FIRMWARE); +MODULE_FIRMWARE(SI2141_A10_FIRMWARE); diff --git a/drivers/media/tuners/si2157_priv.h b/drivers/media/tuners/si2157_priv.h index d6b2c7b44053..e6436f74abaa 100644 --- a/drivers/media/tuners/si2157_priv.h +++ b/drivers/media/tuners/si2157_priv.h @@ -42,6 +42,7 @@ struct si2157_dev { #define SI2157_CHIPTYPE_SI2157 0 #define SI2157_CHIPTYPE_SI2146 1 +#define SI2157_CHIPTYPE_SI2141 2 /* firmware command struct */ #define SI2157_ARGLEN 30 @@ -52,5 +53,6 @@ struct si2157_cmd { }; #define SI2158_A20_FIRMWARE "dvb-tuner-si2158-a20-01.fw" +#define SI2141_A10_FIRMWARE "dvb-tuner-si2141-a10-01.fw" #endif -- 2.11.0
[PATCH 3/3] [media] cxusb: MyGica T230C support
Mygica T230 DVB-T/T2/C USB stick support. It uses the same FX2/Si2168 bridge/demodulator combo as the T230, but uses the Si2141 tuner. Factor out the common code and pass the tuner type and if port as parameter, to avoid duplicating the initialization code. Signed-off-by: Stefan Brüns --- drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/usb/dvb-usb/cxusb.c| 80 ++-- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/drivers/media/dvb-core/dvb-usb-ids.h b/drivers/media/dvb-core/dvb-usb-ids.h index a7a4674ccc40..ce4a3d574dd7 100644 --- a/drivers/media/dvb-core/dvb-usb-ids.h +++ b/drivers/media/dvb-core/dvb-usb-ids.h @@ -380,6 +380,7 @@ #define USB_PID_SONY_PLAYTV0x0003 #define USB_PID_MYGICA_D6890xd811 #define USB_PID_MYGICA_T2300xc688 +#define USB_PID_MYGICA_T230C 0xc689 #define USB_PID_ELGATO_EYETV_DIVERSITY 0x0011 #define USB_PID_ELGATO_EYETV_DTT 0x0021 #define USB_PID_ELGATO_EYETV_DTT_2 0x003f diff --git a/drivers/media/usb/dvb-usb/cxusb.c b/drivers/media/usb/dvb-usb/cxusb.c index 9fd43a37154c..967f4f74309c 100644 --- a/drivers/media/usb/dvb-usb/cxusb.c +++ b/drivers/media/usb/dvb-usb/cxusb.c @@ -1305,7 +1305,9 @@ static int cxusb_mygica_d689_frontend_attach(struct dvb_usb_adapter *adap) return 0; } -static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) +static int cxusb_mygica_t230_common_frontend_attach(struct dvb_usb_adapter *adap, + const char *tuner_name, + u8 tuner_if_port) { struct dvb_usb_device *d = adap->dev; struct cxusb_state *st = d->priv; @@ -1352,12 +1354,12 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) /* attach tuner */ memset(&si2157_config, 0, sizeof(si2157_config)); si2157_config.fe = adap->fe_adap[0].fe; - si2157_config.if_port = 1; + si2157_config.if_port = tuner_if_port; memset(&info, 0, sizeof(struct i2c_board_info)); - strlcpy(info.type, "si2157", I2C_NAME_SIZE); + strlcpy(info.type, tuner_name, I2C_NAME_SIZE); info.addr = 0x60; info.platform_data = &si2157_config; - request_module(info.type); + request_module("si2157"); client_tuner = i2c_new_device(adapter, &info); if (client_tuner == NULL || client_tuner->dev.driver == NULL) { module_put(client_demod->dev.driver->owner); @@ -1376,6 +1378,16 @@ static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) return 0; } +static int cxusb_mygica_t230_frontend_attach(struct dvb_usb_adapter *adap) +{ + return cxusb_mygica_t230_common_frontend_attach(adap, "si2157", 1); +} + +static int cxusb_mygica_t230c_frontend_attach(struct dvb_usb_adapter *adap) +{ + return cxusb_mygica_t230_common_frontend_attach(adap, "si2141", 0); +} + /* * DViCO has shipped two devices with the same USB ID, but only one of them * needs a firmware download. Check the device class details to see if they @@ -1458,6 +1470,7 @@ static struct dvb_usb_device_properties cxusb_aver_a868r_properties; static struct dvb_usb_device_properties cxusb_d680_dmb_properties; static struct dvb_usb_device_properties cxusb_mygica_d689_properties; static struct dvb_usb_device_properties cxusb_mygica_t230_properties; +static struct dvb_usb_device_properties cxusb_mygica_t230c_properties; static int cxusb_probe(struct usb_interface *intf, const struct usb_device_id *id) @@ -1490,6 +1503,8 @@ static int cxusb_probe(struct usb_interface *intf, THIS_MODULE, NULL, adapter_nr) || 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230_properties, THIS_MODULE, NULL, adapter_nr) || + 0 == dvb_usb_device_init(intf, &cxusb_mygica_t230c_properties, +THIS_MODULE, NULL, adapter_nr) || 0) return 0; @@ -1541,6 +1556,7 @@ enum cxusb_table_index { CONEXANT_D680_DMB, MYGICA_D689, MYGICA_T230, + MYGICA_T230C, NR__cxusb_table_index }; @@ -1608,6 +1624,9 @@ static struct usb_device_id cxusb_table[NR__cxusb_table_index + 1] = { [MYGICA_T230] = { USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230) }, + [MYGICA_T230C] = { + USB_DEVICE(USB_VID_CONEXANT, USB_PID_MYGICA_T230C) + }, {} /* Terminating entry */ }; MODULE_DEVICE_TABLE (usb, cxusb_table); @@ -2307,6 +2326,59 @@ static struct dvb_usb_device_properties cxusb_mygica_t230_properties = {
[PATCH 0/3] Add support for MyGica T230C DVB-T2 stick
The required command sequence for the new tuner (Si2141) was traced from the current Windows driver and verified with a small python script/libusb. The changes to the Si2168 and cxusb driver are mostly addition of the required IDs and some glue code. Stefan Brüns (3): [media] si2157: Add support for Si2141-A10 [media] si2168: add support for Si2168-D60 [media] cxusb: MyGica T230C support drivers/media/dvb-core/dvb-usb-ids.h | 1 + drivers/media/dvb-frontends/si2168.c | 4 ++ drivers/media/dvb-frontends/si2168_priv.h | 2 + drivers/media/tuners/si2157.c | 23 - drivers/media/tuners/si2157_priv.h| 2 + drivers/media/usb/dvb-usb/cxusb.c | 80 +-- 6 files changed, 106 insertions(+), 6 deletions(-) -- 2.11.0
[PATCH 2/3] [media] si2168: add support for Si2168-D60
Add handling for new revision, requiring new firmware. Signed-off-by: Stefan Brüns --- drivers/media/dvb-frontends/si2168.c | 4 drivers/media/dvb-frontends/si2168_priv.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/drivers/media/dvb-frontends/si2168.c b/drivers/media/dvb-frontends/si2168.c index 20b4a659e2e4..28f3bbe0af25 100644 --- a/drivers/media/dvb-frontends/si2168.c +++ b/drivers/media/dvb-frontends/si2168.c @@ -674,6 +674,9 @@ static int si2168_probe(struct i2c_client *client, case SI2168_CHIP_ID_B40: dev->firmware_name = SI2168_B40_FIRMWARE; break; + case SI2168_CHIP_ID_D60: + dev->firmware_name = SI2168_D60_FIRMWARE; + break; default: dev_dbg(&client->dev, "unknown chip version Si21%d-%c%c%c\n", cmd.args[2], cmd.args[1], cmd.args[3], cmd.args[4]); @@ -761,3 +764,4 @@ MODULE_LICENSE("GPL"); MODULE_FIRMWARE(SI2168_A20_FIRMWARE); MODULE_FIRMWARE(SI2168_A30_FIRMWARE); MODULE_FIRMWARE(SI2168_B40_FIRMWARE); +MODULE_FIRMWARE(SI2168_D60_FIRMWARE); diff --git a/drivers/media/dvb-frontends/si2168_priv.h b/drivers/media/dvb-frontends/si2168_priv.h index 7843ccb448a0..4baa95b7d648 100644 --- a/drivers/media/dvb-frontends/si2168_priv.h +++ b/drivers/media/dvb-frontends/si2168_priv.h @@ -25,6 +25,7 @@ #define SI2168_A20_FIRMWARE "dvb-demod-si2168-a20-01.fw" #define SI2168_A30_FIRMWARE "dvb-demod-si2168-a30-01.fw" #define SI2168_B40_FIRMWARE "dvb-demod-si2168-b40-01.fw" +#define SI2168_D60_FIRMWARE "dvb-demod-si2168-d60-01.fw" #define SI2168_B40_FIRMWARE_FALLBACK "dvb-demod-si2168-02.fw" /* state struct */ @@ -37,6 +38,7 @@ struct si2168_dev { #define SI2168_CHIP_ID_A20 ('A' << 24 | 68 << 16 | '2' << 8 | '0' << 0) #define SI2168_CHIP_ID_A30 ('A' << 24 | 68 << 16 | '3' << 8 | '0' << 0) #define SI2168_CHIP_ID_B40 ('B' << 24 | 68 << 16 | '4' << 8 | '0' << 0) + #define SI2168_CHIP_ID_D60 ('D' << 24 | 68 << 16 | '6' << 8 | '0' << 0) unsigned int chip_id; unsigned int version; const char *firmware_name; -- 2.11.0
[PATCH v2 2/3] iio: adc: Fix integration time/averaging for INA219/220
INA226/230/231 has integration times per voltage channel and common averaging setting for both channels, while the INA219/220 only has a combined integration time/averaging setting per channel. Only expose the averaging attribute for the INA226, and expose the correct integration times for the INA219. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 204 ++- 1 file changed, 182 insertions(+), 22 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index bba10a1b2fcb..232c0b80d658 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -49,6 +49,7 @@ /* settings - depend on use case */ #define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA219_DEFAULT_IT 532 #define INA226_CONFIG_DEFAULT 0x4327 #define INA226_DEFAULT_AVG 4 #define INA226_DEFAULT_IT 1110 @@ -56,19 +57,24 @@ #define INA2XX_RSHUNT_DEFAULT 1 /* - * bit mask for reading the averaging setting in the configuration register + * bit masks for reading the settings in the configuration register * FIXME: use regmap_fields. */ #define INA2XX_MODE_MASK GENMASK(3, 0) +/* Averaging for VBus/VShunt/Power */ #define INA226_AVG_MASKGENMASK(11, 9) #define INA226_SHIFT_AVG(val) ((val) << 9) /* Integration time for VBus */ +#define INA219_ITB_MASKGENMASK(10, 7) +#define INA219_SHIFT_ITB(val) ((val) << 7) #define INA226_ITB_MASKGENMASK(8, 6) #define INA226_SHIFT_ITB(val) ((val) << 6) /* Integration time for VShunt */ +#define INA219_ITS_MASKGENMASK(6, 3) +#define INA219_SHIFT_ITS(val) ((val) << 3) #define INA226_ITS_MASKGENMASK(5, 3) #define INA226_SHIFT_ITS(val) ((val) << 3) @@ -108,6 +114,7 @@ struct ina2xx_config { int bus_voltage_shift; int bus_voltage_lsb;/* uV */ int power_lsb; /* uW */ + enum ina2xx_ids chip_id; }; struct ina2xx_chip_info { @@ -130,6 +137,7 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_shift = 3, .bus_voltage_lsb = 4000, .power_lsb = 2, + .chip_id = ina219, }, [ina226] = { .config_default = INA226_CONFIG_DEFAULT, @@ -138,6 +146,7 @@ static const struct ina2xx_config ina2xx_config[] = { .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb = 25000, + .chip_id = ina226, }, }; @@ -283,6 +292,66 @@ static int ina226_set_int_time_vshunt(struct ina2xx_chip_info *chip, return 0; } +/* Conversion times in uS. */ +static const int ina219_conv_time_tab_subsample[] = { 84, 148, 276, 532 }; +static const int ina219_conv_time_tab_average[] = { 532, 1060, 2130, 4260, + 8510, 17020, 34050, 68100}; + +static int ina219_lookup_int_time(unsigned int *val_us, int *bits) +{ + if (*val_us > 68100 || *val_us < 84) + return -EINVAL; + + if (*val_us <= 532) { + *bits = find_closest(*val_us, ina219_conv_time_tab_subsample, + ARRAY_SIZE(ina219_conv_time_tab_subsample)); + *val_us = ina219_conv_time_tab_subsample[*bits]; + } else { + *bits = find_closest(*val_us, ina219_conv_time_tab_average, + ARRAY_SIZE(ina219_conv_time_tab_average)); + *val_us = ina219_conv_time_tab_average[*bits]; + *bits |= 0x8; + } + + return 0; +} + +static int ina219_set_int_time_vbus(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vbus = val_us_best; + + *config &= ~INA219_ITB_MASK; + *config |= INA219_SHIFT_ITB(bits) & INA219_ITB_MASK; + + return 0; +} + +static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, + unsigned int val_us, unsigned int *config) +{ + int bits, ret; + unsigned int val_us_best = val_us; + + ret = ina219_lookup_int_time(&val_us_best, &bits); + if (ret) + return ret; + + chip->int_time_vshunt = val_us_best; + + *config &= ~INA219_ITS_MASK; + *config |= INA219_SHIFT_ITS(bits) & INA219_ITS_MASK; + + return 0; +} + static int ina2xx_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -308,10 +377,21 @@
[PATCH v2 1/3] iio: adc: ina2xx: Make use of attribute flags consistent on all channels
Flags for shared channel attributes should be set on all channels of a channel set. I.e. SAMP_FREQUENCY and OVERSAMPLING_RATIO are set on the in_voltage{0,1} channels, thus should be set on in_power, in_current. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 6b588ac3130c..bba10a1b2fcb 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -442,6 +442,8 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ BIT(IIO_CHAN_INFO_SCALE) | \ BIT(IIO_CHAN_INFO_INT_TIME), \ + .info_mask_shared_by_dir = BIT(IIO_CHAN_INFO_SAMP_FREQ) | \ + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \ .scan_index = (_index), \ .scan_type = { \ .sign = 'u', \ -- 2.12.2
[PATCH v2 3/3] iio: adc: Fix polling of INA219 conversion ready flag
While the INA226 has a conversion ready flag (CVRF) in the R/W Mask/Enable register with read-to-clear semantics, the corresponding bit of the INA219 (CNVR) is part of the bus voltage register. The flag is cleared by reading the power register. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 36 +--- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 232c0b80d658..68884d26b50c 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -44,6 +44,7 @@ #define INA226_MASK_ENABLE 0x06 #define INA226_CVRFBIT(3) +#define INA219_CNVRBIT(1) #define INA2XX_MAX_REGISTERS8 @@ -592,6 +593,7 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) int bit, ret, i = 0; s64 time_a, time_b; unsigned int alert; + int cnvr_need_clear = 0; time_a = iio_get_time_ns(indio_dev); @@ -603,22 +605,30 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) * we check the ConVersionReadyFlag. * On hardware that supports using the ALERT pin to toggle a * GPIO a triggered buffer could be used instead. -* For now, we pay for that extra read of the ALERT register +* For now, we do an extra read of the MASK_ENABLE register (INA226) +* resp. the BUS_VOLTAGE register (INA219). */ if (!chip->allow_async_readout) do { - ret = regmap_read(chip->regmap, INA226_MASK_ENABLE, - &alert); + if (chip->config->chip_id == ina226) { + ret = regmap_read(chip->regmap, + INA226_MASK_ENABLE, &alert); + alert &= INA226_CVRF; + } else { + ret = regmap_read(chip->regmap, + INA2XX_BUS_VOLTAGE, &alert); + alert &= INA219_CNVR; + cnvr_need_clear = alert; + } + if (ret < 0) return ret; - alert &= INA226_CVRF; } while (!alert); /* -* Single register reads: bulk_read will not work with ina226 -* as there is no auto-increment of the address register for -* data length longer than 16bits. +* Single register reads: bulk_read will not work with ina226/219 +* as there is no auto-increment of the register pointer. */ for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { @@ -630,6 +640,18 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) return ret; data[i++] = val; + + if (INA2XX_SHUNT_VOLTAGE + bit == INA2XX_POWER) + cnvr_need_clear = 0; + } + + /* Dummy read on INA219 power register to clear CNVR flag */ + if (cnvr_need_clear && chip->config->chip_id == ina219) { + unsigned int val; + + ret = regmap_read(chip->regmap, INA2XX_POWER, &val); + if (ret < 0) + return ret; } time_b = iio_get_time_ns(indio_dev); -- 2.12.2
[PATCH v2 0/3] Enhancements for TI INA219/220 current/voltage monitors
The ina2xx adc driver does not handle differences between INA219/220 and INA226/230/231 completely: - The 219 uses a 4 bit register field for for the bus/shunt voltage adc integration times each, whereas the 226 uses two 3 bit fields for the integration times and an additional 3 bit averaging field. The second patch corrects setting integration times in the config register. - The conversion ready flag differs between the two families. Handling for this flag is corrected in the third patch. Without the patch, the kernel will go in a busy loop (setting read register 6 is ACKed by the INA219, and the following read returns 0). - The 219 also has an adjustable PGA. The default setting allows the maximum range, but also reduces accuracy. (Postponed, needs more thought) Changes v2: - split attribute flags change from [Patch 1] iio: adc: Fix integration time/ averaging from INA219/220 - changed Patch v1 1/2 (now 2/3, feedback by Jonathan Cameron): * Use correct flags for all channels/chips * Improve readability of chip/channel handling in if statement - dropped Patch v1 2/2 for now - added Patch v2 3/3 Stefan Brüns (3): iio: adc: ina2xx: Make use of attribute flags consistent on all channels iio: adc: Fix integration time/averaging for INA219/220 iio: adc: Fix polling of INA219 conversion ready flag drivers/iio/adc/ina2xx-adc.c | 242 +-- 1 file changed, 213 insertions(+), 29 deletions(-) -- 2.12.2
[PATCH v3 3/4] iio: light: vl6180: Cleanup als_gain lookup, avoid register readback
Instead of manually iterating the array of allowed gain values, use find_closest. Storing the current gain setting avoids accessing the hardware on each query. Signed-off-by: Stefan Brüns --- Changes in v3: None Changes in v2: - Add missing spaces drivers/iio/light/vl6180.c | 85 -- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 9b056c83a90a..49e9f92cd116 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,7 @@ struct vl6180_data { struct i2c_client *client; struct mutex lock; + unsigned int als_gain_milli; unsigned int als_it_ms; }; @@ -276,19 +278,17 @@ static const struct iio_chan_spec vl6180_channels[] = { }; /* - * Columns 3 & 4 represent the same value in decimal and hex notations. - * Kept in order to avoid the datatype conversion while reading the - * hardware_gain. + * Available Ambient Light Sensor gain settings, 1/1000th, and + * corresponding setting for the VL6180_ALS_GAIN register */ -static const int vl6180_als_gain[8][4] = { - { 1,0, 70, VL6180_ALS_GAIN_1 }, - { 1,25, 69, VL6180_ALS_GAIN_1_25 }, - { 1,67, 68, VL6180_ALS_GAIN_1_67 }, - { 2,50, 67, VL6180_ALS_GAIN_2_5 }, - { 5,0, 66, VL6180_ALS_GAIN_5 }, - { 10, 0, 65, VL6180_ALS_GAIN_10 }, - { 20, 0, 64, VL6180_ALS_GAIN_20 }, - { 40, 0, 71, VL6180_ALS_GAIN_40 } +static const int vl6180_als_gain_tab[8] = { + 1000, 1250, 1670, 2500, 5000, 1, 2, 4 +}; +static const u8 vl6180_als_gain_tab_bits[8] = { + VL6180_ALS_GAIN_1,VL6180_ALS_GAIN_1_25, + VL6180_ALS_GAIN_1_67, VL6180_ALS_GAIN_2_5, + VL6180_ALS_GAIN_5,VL6180_ALS_GAIN_10, + VL6180_ALS_GAIN_20, VL6180_ALS_GAIN_40 }; static int vl6180_read_raw(struct iio_dev *indio_dev, @@ -296,7 +296,7 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct vl6180_data *data = iio_priv(indio_dev); - int ret, i; + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -328,17 +328,11 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_HARDWAREGAIN: - ret = vl6180_read_byte(data->client, VL6180_ALS_GAIN); - if (ret < 0) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { - if (ret == vl6180_als_gain[i][2]) { - *val = vl6180_als_gain[i][0]; - *val2 = vl6180_als_gain[i][1]; - } - } + *val = data->als_gain_milli; + *val2 = 1000; + + return IIO_VAL_FRACTIONAL; - return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -364,25 +358,33 @@ static int vl6180_hold(struct vl6180_data *data, bool hold) static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2) { - int i, ret; - - for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { - if (val == vl6180_als_gain[i][0] && - val2 == vl6180_als_gain[i][1]) { - mutex_lock(&data->lock); - ret = vl6180_hold(data, true); - if (ret < 0) - goto fail; - ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN, - vl6180_als_gain[i][3]); -fail: - vl6180_hold(data, false); - mutex_unlock(&data->lock); - return ret; - } - } + int i, ret, gain; + + if (val < 1 || val > 40) + return -EINVAL; - return -EINVAL; + gain = (val * 100 + val2) / 1000; + if (gain < 1 || gain > 4) + return -EINVAL; + + i = find_closest(gain, vl6180_als_gain_tab, +ARRAY_SIZE(vl6180_als_gain_tab)); + + mutex_lock(&data->lock); + ret = vl6180_hold(data, true); + if (ret < 0) + goto fail; + + ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN, + vl6180_als_gain_tab_bits[i]); + + if (ret >= 0) + data->als_gain_milli = vl6180_als_gain_tab[i]; + +fail: + vl6180_hold(data, false); + mutex_unlock(&data->lock); + return ret; } static int vl6180_set_it(struct vl6180_data *data, i
[PATCH v3 1/4] iio: light: vl6180: Move range check to integration time setter, cleanup
This improves code uniformity (range checks for als_gain are also done in the setter). Also unmangle rounding and calculation of register value. The calculated integration time it_ms is required in the next patch of the series. Signed-off-by: Stefan Brüns --- Changes in v3: - Drop whitespace change Changes in v2: - Removed redundant parenthesis drivers/iio/light/vl6180.c | 17 ++--- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 6e25b724d941..3b6351b89ce7 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -386,16 +386,21 @@ static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2) return -EINVAL; } -static int vl6180_set_it(struct vl6180_data *data, int val2) +static int vl6180_set_it(struct vl6180_data *data, int val, int val2) { - int ret; + int ret, it_ms; + + it_ms = (val2 + 500) / 1000; /* round to ms */ + if (val != 0 || it_ms < 1 || it_ms > 512) + return -EINVAL; mutex_lock(&data->lock); ret = vl6180_hold(data, true); if (ret < 0) goto fail; - ret = vl6180_write_word(data->client, VL6180_ALS_IT, - (val2 - 500) / 1000); /* write value in ms */ + + ret = vl6180_write_word(data->client, VL6180_ALS_IT, it_ms - 1); + fail: vl6180_hold(data, false); mutex_unlock(&data->lock); @@ -411,10 +416,8 @@ static int vl6180_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: - if (val != 0 || val2 < 500 || val2 >= 512500) - return -EINVAL; + return vl6180_set_it(data, val, val2); - return vl6180_set_it(data, val2); case IIO_CHAN_INFO_HARDWAREGAIN: if (chan->type != IIO_LIGHT) return -EINVAL; -- 2.14.1
[PATCH v3 4/4] iio: light: vl6180: Correct ALS scale for non-default gain/integration time
The reported scale was only correct for the default settings of 100 ms integration time and gain 1. This aligns the reported scale with the behaviour of any other IIO driver and the documented ABI, but may require userspace changes if someone uses non-default settings. Signed-off-by: Stefan Brüns --- Changes in v3: None Changes in v2: None drivers/iio/light/vl6180.c | 9 ++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 49e9f92cd116..67f8beb84fc3 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -315,9 +315,12 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_LIGHT: - *val = 0; /* one ALS count is 0.32 Lux */ - *val2 = 32; - break; + /* one ALS count is 0.32 Lux @ gain 1, IT 100 ms */ + *val = 32000; /* 0.32 * 1000 * 100 */ + *val2 = data->als_gain_milli * data->als_it_ms; + + return IIO_VAL_FRACTIONAL; + case IIO_DISTANCE: *val = 0; /* sensor reports mm, scale to meter */ *val2 = 1000; -- 2.14.1
[PATCH v3 0/4] Currently, the vl6180 driver reports bogus in_illuminance_scale values
when the integration time or hardware gain are changed from its default value, i.e. it always reports a fixed value. To avoid readback of the register values in case integration time, gain or scale is queried, save the register values. Use the saved values to report the correct scale value. Changes in v3: - Drop whitespace change - Use IIO_VAL_FRACTIONAL for integration time return value Changes in v2: - Removed redundant parenthesis - Add missing spaces Stefan Brüns (4): iio: light: vl6180: Move range check to integration time setter, cleanup iio: light: vl6180: Avoid readback of integration time register iio: light: vl6180: Cleanup als_gain lookup, avoid register readback iio: light: vl6180: Correct ALS scale for non-default gain/integration time drivers/iio/light/vl6180.c | 126 + 1 file changed, 69 insertions(+), 57 deletions(-) -- 2.14.1
[PATCH v3 2/4] iio: light: vl6180: Avoid readback of integration time register
Instead of reading the value from the register on each query, store the set value. Signed-off-by: Stefan Brüns --- Changes in v3: - Use IIO_VAL_FRACTIONAL for integration time return value Changes in v2: None drivers/iio/light/vl6180.c | 15 +-- 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 3b6351b89ce7..9b056c83a90a 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -86,6 +86,7 @@ struct vl6180_data { struct i2c_client *client; struct mutex lock; + unsigned int als_it_ms; }; enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX }; @@ -306,13 +307,11 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: - ret = vl6180_read_word(data->client, VL6180_ALS_IT); - if (ret < 0) - return ret; - *val = 0; /* 1 count = 1ms (0 = 1ms) */ - *val2 = (ret + 1) * 1000; /* convert to seconds */ + *val = data->als_it_ms; + *val2 = 1000; + + return IIO_VAL_FRACTIONAL; - return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_LIGHT: @@ -401,6 +400,9 @@ static int vl6180_set_it(struct vl6180_data *data, int val, int val2) ret = vl6180_write_word(data->client, VL6180_ALS_IT, it_ms - 1); + if (ret >= 0) + data->als_it_ms = it_ms; + fail: vl6180_hold(data, false); mutex_unlock(&data->lock); @@ -471,6 +473,7 @@ static int vl6180_init(struct vl6180_data *data) return ret; /* ALS integration time: 100ms */ + data->als_it_ms = 100; ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100); if (ret < 0) return ret; -- 2.14.1
[PATCH v3 10/10] arm64: allwinner: a64: add dma controller references to spi nodes
The spi controller nodes omit the dma controller/channel references, add it. This does not yet enable DMA for SPI transfers, as the spi-sun6i driver lacks support for DMA, but always uses PIO to the FIFO. Signed-off-by: Stefan Brüns --- Changes in v3: None Changes in v2: None arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 4 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index cbffefce0e71..a6a5a40d76d0 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -476,6 +476,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; clock-names = "ahb", "mod"; + dmas = <&dma 23>, <&dma 23>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi0_pins>; resets = <&ccu RST_BUS_SPI0>; @@ -491,6 +493,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; clock-names = "ahb", "mod"; + dmas = <&dma 24>, <&dma 24>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi1_pins>; resets = <&ccu RST_BUS_SPI1>; -- 2.14.1
[PATCH v3 05/10] dmaengine: sun6i: Move number of pchans/vchans/request to device struct
Preparatory patch: If the same compatible is used for different SoCs which have a common register layout, but different number of channels, the channel count can no longer be stored in the config. Store it in the device structure instead. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v3: None Changes in v2: None drivers/dma/sun6i-dma.c | 26 -- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index c7ad3b4e836c..b08b89fa4679 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -185,6 +185,9 @@ struct sun6i_dma_dev { struct sun6i_pchan *pchans; struct sun6i_vchan *vchans; const struct sun6i_dma_config *cfg; + u32 num_pchans; + u32 num_vchans; + u32 max_request; }; static struct device *chan2dev(struct dma_chan *chan) @@ -431,7 +434,6 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan) static void sun6i_dma_tasklet(unsigned long data) { struct sun6i_dma_dev *sdev = (struct sun6i_dma_dev *)data; - const struct sun6i_dma_config *cfg = sdev->cfg; struct sun6i_vchan *vchan; struct sun6i_pchan *pchan; unsigned int pchan_alloc = 0; @@ -459,7 +461,7 @@ static void sun6i_dma_tasklet(unsigned long data) } spin_lock_irq(&sdev->lock); - for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { + for (pchan_idx = 0; pchan_idx < sdev->num_pchans; pchan_idx++) { pchan = &sdev->pchans[pchan_idx]; if (pchan->vchan || list_empty(&sdev->pending)) @@ -480,7 +482,7 @@ static void sun6i_dma_tasklet(unsigned long data) } spin_unlock_irq(&sdev->lock); - for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { + for (pchan_idx = 0; pchan_idx < sdev->num_pchans; pchan_idx++) { if (!(pchan_alloc & BIT(pchan_idx))) continue; @@ -502,7 +504,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id) int i, j, ret = IRQ_NONE; u32 status; - for (i = 0; i < sdev->cfg->nr_max_channels / DMA_IRQ_CHAN_NR; i++) { + for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) { status = readl(sdev->base + DMA_IRQ_STAT(i)); if (!status) continue; @@ -982,7 +984,7 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec, struct dma_chan *chan; u8 port = dma_spec->args[0]; - if (port > sdev->cfg->nr_max_requests) + if (port > sdev->max_request) return NULL; chan = dma_get_any_slave_channel(&sdev->slave); @@ -1015,7 +1017,7 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev) { int i; - for (i = 0; i < sdev->cfg->nr_max_vchans; i++) { + for (i = 0; i < sdev->num_vchans; i++) { struct sun6i_vchan *vchan = &sdev->vchans[i]; list_del(&vchan->vc.chan.device_node); @@ -1221,26 +1223,30 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; sdc->slave.dev = &pdev->dev; - sdc->pchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_channels, + sdc->num_pchans = sdc->cfg->nr_max_channels; + sdc->num_vchans = sdc->cfg->nr_max_vchans; + sdc->max_request = sdc->cfg->nr_max_requests; + + sdc->pchans = devm_kcalloc(&pdev->dev, sdc->num_pchans, sizeof(struct sun6i_pchan), GFP_KERNEL); if (!sdc->pchans) return -ENOMEM; - sdc->vchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_vchans, + sdc->vchans = devm_kcalloc(&pdev->dev, sdc->num_vchans, sizeof(struct sun6i_vchan), GFP_KERNEL); if (!sdc->vchans) return -ENOMEM; tasklet_init(&sdc->task, sun6i_dma_tasklet, (unsigned long)sdc); - for (i = 0; i < sdc->cfg->nr_max_channels; i++) { + for (i = 0; i < sdc->num_pchans; i++) { struct sun6i_pchan *pchan = &sdc->pchans[i]; pchan->idx = i; pchan->base = sdc->base + 0x100 + i * 0x40; } - for (i = 0; i < sdc->cfg->nr_max_vchans; i++) { + for (i = 0; i < sdc->num_vchans; i++) { struct sun6i_vchan *vchan = &sdc->vchans[i]; INIT_LIST_HEAD(&vchan->node); -- 2.14.1
[PATCH v3 02/10] dmaengine: sun6i: Correct burst length field offsets for H3
For the H3, the burst lengths field offsets in the channel configuration register differs from earlier SoC generations. Using the A31 register macros actually configured the H3 controller do to bursts of length 1 always, which although working leads to higher bus utilisation. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v3: None Changes in v2: - Use controller specific callback for burst length setting drivers/dma/sun6i-dma.c | 36 +++- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index b4a29d1a100d..269d4ea194e8 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -68,13 +68,15 @@ #define DMA_CHAN_CFG_SRC_DRQ(x)((x) & 0x1f) #define DMA_CHAN_CFG_SRC_IO_MODE BIT(5) #define DMA_CHAN_CFG_SRC_LINEAR_MODE (0 << 5) -#define DMA_CHAN_CFG_SRC_BURST(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_A31(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_H3(x) (((x) & 0x3) << 6) #define DMA_CHAN_CFG_SRC_WIDTH(x) (((x) & 0x3) << 9) #define DMA_CHAN_CFG_DST_DRQ(x)(DMA_CHAN_CFG_SRC_DRQ(x) << 16) #define DMA_CHAN_CFG_DST_IO_MODE (DMA_CHAN_CFG_SRC_IO_MODE << 16) #define DMA_CHAN_CFG_DST_LINEAR_MODE (DMA_CHAN_CFG_SRC_LINEAR_MODE << 16) -#define DMA_CHAN_CFG_DST_BURST(x) (DMA_CHAN_CFG_SRC_BURST(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_A31(x) (DMA_CHAN_CFG_SRC_BURST_A31(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_H3(x) (DMA_CHAN_CFG_SRC_BURST_H3(x) << 16) #define DMA_CHAN_CFG_DST_WIDTH(x) (DMA_CHAN_CFG_SRC_WIDTH(x) << 16) #define DMA_CHAN_CUR_SRC 0x10 @@ -115,6 +117,7 @@ struct sun6i_dma_config { * BSP kernel source code. */ void (*clock_autogate_enable)(); + void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); }; /* @@ -280,6 +283,18 @@ static void sun6i_enable_clock_autogate_h3(struct sun6i_dma_dev *sdev) writel(SUNXI_H3_DMA_GATE_ENABLE, sdev->base + SUNXI_H3_DMA_GATE); } +static void sun6i_set_burst_length_a31(u32 *p_cfg, s8 src_burst, s8 dst_burst) +{ + *p_cfg |= DMA_CHAN_CFG_SRC_BURST_A31(src_burst) | + DMA_CHAN_CFG_DST_BURST_A31(dst_burst); +} + +static void sun6i_set_burst_length_h3(u32 *p_cfg, s8 src_burst, s8 dst_burst) +{ + *p_cfg |= DMA_CHAN_CFG_SRC_BURST_H3(src_burst) | + DMA_CHAN_CFG_DST_BURST_H3(dst_burst); +} + static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) { struct sun6i_desc *txd = pchan->desc; @@ -559,11 +574,11 @@ static int set_config(struct sun6i_dma_dev *sdev, if (dst_width < 0) return dst_width; - *p_cfg = DMA_CHAN_CFG_SRC_BURST(src_burst) | - DMA_CHAN_CFG_SRC_WIDTH(src_width) | - DMA_CHAN_CFG_DST_BURST(dst_burst) | + *p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) | DMA_CHAN_CFG_DST_WIDTH(dst_width); + sdev->cfg->set_burst_length(p_cfg, src_burst, dst_burst); + return 0; } @@ -606,11 +621,11 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) | DMA_CHAN_CFG_DST_LINEAR_MODE | DMA_CHAN_CFG_SRC_LINEAR_MODE | - DMA_CHAN_CFG_SRC_BURST(burst) | DMA_CHAN_CFG_SRC_WIDTH(width) | - DMA_CHAN_CFG_DST_BURST(burst) | DMA_CHAN_CFG_DST_WIDTH(width); + sdev->cfg->set_burst_length(v_lli->cfg, burst, burst); + sun6i_dma_lli_add(NULL, v_lli, p_lli, txd); sun6i_dma_dump_lli(vchan, v_lli); @@ -1022,6 +1037,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .nr_max_channels = 16, .nr_max_requests = 30, .nr_max_vchans = 53, + .set_burst_length = sun6i_set_burst_length_a31; }; /* @@ -1034,6 +1050,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_requests = 24, .nr_max_vchans = 37, .clock_autogate_enable = sun6i_enable_clock_autogate_a23; + .set_burst_length = sun6i_set_burst_length_a31; }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { @@ -1041,13 +1058,12 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .nr_max_requests = 28, .nr_max_vchans = 39, .clock_autogate_enable = sun6i_enable_clock_autogate_a23; + .set_burst_length = sun6i_set_burst_length_a31; }; /* * The H3 has 12 physical channels, a maximum DRQ port id of 27, * and a total of 34 usable source and destination endpoints. - * It also supports additional burst lengths and bus widths, - * and the burst length fields have different offsets. */ static struct sun6i_dma_config sun8i_h3_dma_cfg = { @@ -1055,6 +1071,7 @@ static struct sun6i_dma_conf
[PATCH v3 09/10] arm64: allwinner: a64: Add device node for DMA controller
The A64 SoC has a DMA controller that supports 8 DMA channels to and from various peripherals. The last used DRQ port is 27. Add a device node for it. Signed-off-by: Stefan Brüns --- Changes in v3: - Drop leading 0 from dma controller unit name Changes in v2: None arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 11 +++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index e9a9d7fb01c8..cbffefce0e71 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -136,6 +136,17 @@ reg = <0x01c0 0x1000>; }; + dma: dma-controller@1c02000 { + compatible = "allwinner,sun50i-a64-dma"; + reg = <0x01c02000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_DMA>; + dma-channels = <8>; + dma-requests = <27>; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + }; + mmc0: mmc@1c0f000 { compatible = "allwinner,sun50i-a64-mmc"; reg = <0x01c0f000 0x1000>; -- 2.14.1
[PATCH v3 06/10] arm64: allwinner: a64: Add devicetree binding for DMA controller
The A64 is register compatible with the H3, but has a different number of dma channels and request ports. Attach additional properties to the node to allow future reuse of the compatible for controllers with different number of channels/requests. If dma-requests is not specified, the register layout defined maximum of 32 is used. Signed-off-by: Stefan Brüns Acked-by: Rob Herring --- Changes in v3: - Drop leading 0 from unit name in DT example Changes in v2: None .../devicetree/bindings/dma/sun6i-dma.txt | 28 +- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt index 98fbe1a5c6dd..b2df4f0f1488 100644 --- a/Documentation/devicetree/bindings/dma/sun6i-dma.txt +++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt @@ -18,7 +18,7 @@ Required properties: - #dma-cells : Should be 1, a single cell holding a line request number Example: - dma: dma-controller@01c02000 { + dma: dma-controller@1c02000 { compatible = "allwinner,sun6i-a31-dma"; reg = <0x01c02000 0x1000>; interrupts = <0 50 4>; @@ -27,6 +27,32 @@ Example: #dma-cells = <1>; }; +-- +For A64 DMA controller: + +Required properties: +- compatible: "allwinner,sun50i-a64-dma" +- dma-channels: Number of DMA channels supported by the controller. + Refer to Documentation/devicetree/bindings/dma/dma.txt +- all properties above, i.e. reg, interrupts, clocks, resets and #dma-cells + +Optional properties: +- dma-requests: Number of DMA request signals supported by the controller. + Refer to Documentation/devicetree/bindings/dma/dma.txt + +Example: + dma: dma-controller@1c02000 { + compatible = "allwinner,sun50i-a64-dma"; + reg = <0x01c02000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_DMA>; + dma-channels = <8>; + dma-requests = <27>; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + }; +-- + Clients: DMA clients connected to the A31 DMA controller must use the format -- 2.14.1
[PATCH v3 08/10] dmaengine: sun6i: Add support for Allwinner A64 and compatibles
The A64 SoC has the same dma engine as the H3 (sun8i), with a reduced amount of physical channels. To allow future reuse of the compatible, leave the channel count etc. in the config data blank and retrieve it from the devicetree. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v3: - Omit default values from sun50i_a64_dma_cfg definition Changes in v2: None drivers/dma/sun6i-dma.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 34d70af442ff..b4e759f4aa75 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -1122,6 +1122,25 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); }; +/* + * The A64 binding uses the number of dma channels from the + * device tree node. + */ +static struct sun6i_dma_config sun50i_a64_dma_cfg = { + .clock_autogate_enable = sun6i_enable_clock_autogate_h3; + .set_burst_length = sun6i_set_burst_length_h3; + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); +}; + /* * The V3s have only 8 physical channels, a maximum DRQ port id of 23, * and a total of 24 usable source and destination endpoints. @@ -1149,6 +1168,7 @@ static const struct of_device_id sun6i_dma_match[] = { { .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg }, { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg }, { .compatible = "allwinner,sun8i-v3s-dma", .data = &sun8i_v3s_dma_cfg }, + { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun6i_dma_match); -- 2.14.1
[PATCH v3 07/10] dmaengine: sun6i: Retrieve channel count/max request from devicetree
To avoid introduction of a new compatible for each small SoC/DMA controller variation, move the definition of the channel count to the devicetree. The number of vchans is no longer explicit, but limited by the highest port/DMA request number. The result is a slight overallocation for SoCs with a sparse port mapping. Signed-off-by: Stefan Brüns --- Changes in v3: None Changes in v2: - Set default number of dma-request if not provided in config or devicetree drivers/dma/sun6i-dma.c | 37 - 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index b08b89fa4679..34d70af442ff 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -42,6 +42,9 @@ #define DMA_STAT 0x30 +/* Offset between DMA_IRQ_EN and DMA_IRQ_STAT limits number of channels */ +#define DMA_MAX_CHANNELS (DMA_IRQ_CHAN_NR * 0x10 / 4) + /* * sun8i specific registers */ @@ -65,7 +68,8 @@ #define DMA_CHAN_LLI_ADDR 0x08 #define DMA_CHAN_CUR_CFG 0x0c -#define DMA_CHAN_CFG_SRC_DRQ(x)((x) & 0x1f) +#define DMA_CHAN_MAX_DRQ 0x1f +#define DMA_CHAN_CFG_SRC_DRQ(x)((x) & DMA_CHAN_MAX_DRQ) #define DMA_CHAN_CFG_SRC_IO_MODE BIT(5) #define DMA_CHAN_CFG_SRC_LINEAR_MODE (0 << 5) #define DMA_CHAN_CFG_SRC_BURST_A31(x) (((x) & 0x3) << 7) @@ -1152,6 +1156,7 @@ MODULE_DEVICE_TABLE(of, sun6i_dma_match); static int sun6i_dma_probe(struct platform_device *pdev) { const struct of_device_id *device; + struct device_node *np = pdev->dev.of_node; struct sun6i_dma_dev *sdc; struct resource *res; int ret, i; @@ -1227,6 +1232,36 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->num_vchans = sdc->cfg->nr_max_vchans; sdc->max_request = sdc->cfg->nr_max_requests; + ret = of_property_read_u32(np, "dma-channels", &sdc->num_pchans); + if (ret && !sdc->num_pchans) { + dev_err(&pdev->dev, "Can't get dma-channels.\n"); + return ret; + } + + if (sdc->num_pchans > DMA_MAX_CHANNELS) { + dev_err(&pdev->dev, "Number of dma-channels out of range.\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "dma-requests", &sdc->max_request); + if (ret && !sdc->max_request) { + dev_info(&pdev->dev, "Missing dma-requests, using %u.\n", +DMA_CHAN_MAX_DRQ); + sdc->max_request = DMA_CHAN_MAX_DRQ; + } + + if (sdc->max_request > DMA_CHAN_MAX_DRQ) { + dev_err(&pdev->dev, "Value of dma-requests out of range.\n"); + return -EINVAL; + } + + /* +* If the number of vchans is not specified, derive it from the +* highest port number, at most one channel per port and direction. +*/ + if (!sdc->num_vchans) + sdc->num_vchans = 2 * (sdc->max_request + 1); + sdc->pchans = devm_kcalloc(&pdev->dev, sdc->num_pchans, sizeof(struct sun6i_pchan), GFP_KERNEL); if (!sdc->pchans) -- 2.14.1
[PATCH v3 03/10] dmaengine: sun6i: Restructure code to allow extension for new SoCs
The current code mixes three distinct operations when transforming the slave config to register settings: 1. special handling of DMA_SLAVE_BUSWIDTH_UNDEFINED, maxburst == 0 2. range checking 3. conversion of raw to register values As the range checks depend on the specific SoC, move these out of the conversion to distinct operations. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v3: None Changes in v2: - Store burst lengths in config instead of device structure drivers/dma/sun6i-dma.c | 66 - 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 269d4ea194e8..5d0c009bad02 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -118,6 +118,8 @@ struct sun6i_dma_config { */ void (*clock_autogate_enable)(); void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); + u32 src_burst_lengths; + u32 dst_burst_lengths; }; /* @@ -266,10 +268,6 @@ static inline s8 convert_burst(u32 maxburst) static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { - if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) || - (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) - return -EINVAL; - return addr_width >> 1; } @@ -538,41 +536,43 @@ static int set_config(struct sun6i_dma_dev *sdev, enum dma_transfer_direction direction, u32 *p_cfg) { + enum dma_slave_buswidth src_addr_width, dst_addr_width; + u32 src_maxburst, dst_maxburst; s8 src_width, dst_width, src_burst, dst_burst; + src_addr_width = sconfig->src_addr_width; + dst_addr_width = sconfig->dst_addr_width; + src_maxburst = sconfig->src_maxburst; + dst_maxburst = sconfig->dst_maxburst; + switch (direction) { case DMA_MEM_TO_DEV: - src_burst = convert_burst(sconfig->src_maxburst ? - sconfig->src_maxburst : 8); - src_width = convert_buswidth(sconfig->src_addr_width != - DMA_SLAVE_BUSWIDTH_UNDEFINED ? - sconfig->src_addr_width : - DMA_SLAVE_BUSWIDTH_4_BYTES); - dst_burst = convert_burst(sconfig->dst_maxburst); - dst_width = convert_buswidth(sconfig->dst_addr_width); + if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + src_maxburst = src_maxburst ? src_maxburst : 8; break; case DMA_DEV_TO_MEM: - src_burst = convert_burst(sconfig->src_maxburst); - src_width = convert_buswidth(sconfig->src_addr_width); - dst_burst = convert_burst(sconfig->dst_maxburst ? - sconfig->dst_maxburst : 8); - dst_width = convert_buswidth(sconfig->dst_addr_width != - DMA_SLAVE_BUSWIDTH_UNDEFINED ? - sconfig->dst_addr_width : - DMA_SLAVE_BUSWIDTH_4_BYTES); + if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dst_maxburst = dst_maxburst ? dst_maxburst : 8; break; default: return -EINVAL; } - if (src_burst < 0) - return src_burst; - if (src_width < 0) - return src_width; - if (dst_burst < 0) - return dst_burst; - if (dst_width < 0) - return dst_width; + if (!(BIT(src_addr_width) & sdev->slave.src_addr_widths)) + return -EINVAL; + if (!(BIT(dst_addr_width) & sdev->slave.dst_addr_widths)) + return -EINVAL; + if (!(BIT(src_maxburst) & sdev->cfg->src_burst_lengths)) + return -EINVAL; + if (!(BIT(dst_maxburst) & sdev->cfg->dst_burst_lengths)) + return -EINVAL; + + src_width = convert_buswidth(src_addr_width); + dst_width = convert_buswidth(dst_addr_width); + dst_burst = convert_burst(dst_maxburst); + src_burst = convert_burst(src_maxburst); *p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) | DMA_CHAN_CFG_DST_WIDTH(dst_width); @@ -1038,6 +1038,8 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .nr_max_requests = 30, .nr_max_vchans = 53, .set_burst_length = sun6i_set_burst_length_a31; + .src_burst_lengths = BIT(1) | BIT(8); + .dst_burst_lengths = BIT(1) | BIT(8); }; /* @@ -1051,6 +1053,8 @@ static struct sun6i_dma_config sun8i_a23_d
[PATCH v3 04/10] dmaengine: sun6i: Enable additional burst lengths/widths on H3
The H3 supports bursts lengths of 1, 4, 8 and 16 transfers, each with a width of 1, 2, 4 or 8 bytes. The register value for the the width is log2-encoded, change the conversion function to provide the correct value for width == 8. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v3: None Changes in v2: - Store burst widths in config drivers/dma/sun6i-dma.c | 54 - 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 5d0c009bad02..c7ad3b4e836c 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -120,6 +120,8 @@ struct sun6i_dma_config { void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); u32 src_burst_lengths; u32 dst_burst_lengths; + u32 src_addr_widths; + u32 dst_addr_widths; }; /* @@ -259,8 +261,12 @@ static inline s8 convert_burst(u32 maxburst) switch (maxburst) { case 1: return 0; + case 4: + return 1; case 8: return 2; + case 16: + return 3; default: return -EINVAL; } @@ -268,7 +274,7 @@ static inline s8 convert_burst(u32 maxburst) static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { - return addr_width >> 1; + return ilog2(addr_width); } static void sun6i_enable_clock_autogate_a23(struct sun6i_dma_dev *sdev) @@ -1040,6 +1046,12 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); }; /* @@ -1055,6 +1067,12 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { @@ -1065,11 +1083,19 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); }; /* * The H3 has 12 physical channels, a maximum DRQ port id of 27, * and a total of 34 usable source and destination endpoints. + * It also supports additional burst lengths and bus widths, + * and the burst length fields have different offsets. */ static struct sun6i_dma_config sun8i_h3_dma_cfg = { @@ -1078,8 +1104,16 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_vchans = 34, .clock_autogate_enable = sun6i_enable_clock_autogate_h3; .set_burst_length = sun6i_set_burst_length_h3; - .src_burst_lengths = BIT(1) | BIT(8); - .dst_burst_lengths = BIT(1) | BIT(8); + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); }; /* @@ -1095,6 +1129,12 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2
[PATCH v3 00/10] Commit 3a03ea763a67 ("dmaengine: sun6i: Add support for Allwinner A83T
(sun8i) variant") and commit f008db8c00c1 ("dmaengine: sun6i: Add support for Allwinner H3 (sun8i) variant") added support for the A83T resp. H3, but missed some differences between the original A31 and A83T/H3. The first patch adds a callback to the controller config to set the clock autogating register of different SoC generations, i.e. A31, A23+A83T, H3+later, and uses it to for the correct clock autogating setting. The second patch adds a callback for the burst length setting in the channel config register, which has different field offsets and new burst widths/lengths, which differs between H3 and earlier generations The third patch restructures some code required for the fourth patch and adds the burst lengths to the controller config. The fourth patch adds the burst widths to the config and adds the handling of the H3 specific burst widths. Patch 5 restructures the code to decouple some controller details (e.g. channel count) from the compatible string/the config. Patches 6, 7 and 8 introduce and use the "dma-chans" property for the A64. Although register compatible to the H3, the channel count differs and thus it requires a new compatible. To avoid introduction of new compatibles for each minor variation, anything but the register model is moved to devicetree properties. There is at least one SoC (R40) which can then reuse the A64 compatible, the same would have worked for A83T+V3s. Patches 9 and 10 add the DMA controller node to the devicetree and add the DMA controller reference to the SPI nodes. This patch series could be called v2, but the patches were split and significantly restructured, thus listing changes individually is not to meaningful. Changes in v3: - Check for callback instead of using a no-op callback - Drop leading 0 from unit name in DT example - Omit default values from sun50i_a64_dma_cfg definition - Drop leading 0 from dma controller unit name Changes in v2: - Use callback for autogating instead of variable for different SoC generations - Use controller specific callback for burst length setting - Store burst lengths in config instead of device structure - Store burst widths in config - Set default number of dma-request if not provided in config or devicetree Stefan Brüns (10): dmaengine: sun6i: Correct setting of clock autogating register for A83T/H3 dmaengine: sun6i: Correct burst length field offsets for H3 dmaengine: sun6i: Restructure code to allow extension for new SoCs dmaengine: sun6i: Enable additional burst lengths/widths on H3 dmaengine: sun6i: Move number of pchans/vchans/request to device struct arm64: allwinner: a64: Add devicetree binding for DMA controller dmaengine: sun6i: Retrieve channel count/max request from devicetree dmaengine: sun6i: Add support for Allwinner A64 and compatibles arm64: allwinner: a64: Add device node for DMA controller arm64: allwinner: a64: add dma controller references to spi nodes .../devicetree/bindings/dma/sun6i-dma.txt | 28 ++- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 15 ++ drivers/dma/sun6i-dma.c| 258 - 3 files changed, 242 insertions(+), 59 deletions(-) -- 2.14.1
[PATCH v3 01/10] dmaengine: sun6i: Correct setting of clock autogating register for A83T/H3
The H83T uses a compatible string different from the A23, but requires the same clock autogating register setting. The H3 also requires setting the clock autogating register, but has the register at a different offset. Add three suitable callbacks for the existing controller generations and set it in the controller config structure. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v3: - Check for callback instead of using a no-op callback Changes in v2: - Use callback for autogating instead of variable for different SoC generations drivers/dma/sun6i-dma.c | 27 ++- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index bcd496edc70f..b4a29d1a100d 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -48,6 +48,9 @@ #define SUN8I_DMA_GATE 0x20 #define SUN8I_DMA_GATE_ENABLE 0x4 +#define SUNXI_H3_SECURE_REG0x20 +#define SUNXI_H3_DMA_GATE 0x28 +#define SUNXI_H3_DMA_GATE_ENABLE 0x4 /* * Channels specific registers */ @@ -111,7 +114,7 @@ struct sun6i_dma_config { * however these SoCs really have and need this bit, as seen in the * BSP kernel source code. */ - bool gate_needed; + void (*clock_autogate_enable)(); }; /* @@ -267,6 +270,16 @@ static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) return addr_width >> 1; } +static void sun6i_enable_clock_autogate_a23(struct sun6i_dma_dev *sdev) +{ + writel(SUN8I_DMA_GATE_ENABLE, sdev->base + SUN8I_DMA_GATE); +} + +static void sun6i_enable_clock_autogate_h3(struct sun6i_dma_dev *sdev) +{ + writel(SUNXI_H3_DMA_GATE_ENABLE, sdev->base + SUNXI_H3_DMA_GATE); +} + static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) { struct sun6i_desc *txd = pchan->desc; @@ -1020,24 +1033,28 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 24, .nr_max_vchans = 37, - .gate_needed = true, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23; }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 28, .nr_max_vchans = 39, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23; }; /* * The H3 has 12 physical channels, a maximum DRQ port id of 27, * and a total of 34 usable source and destination endpoints. + * It also supports additional burst lengths and bus widths, + * and the burst length fields have different offsets. */ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_channels = 12, .nr_max_requests = 27, .nr_max_vchans = 34, + .clock_autogate_enable = sun6i_enable_clock_autogate_h3; }; /* @@ -1049,7 +1066,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 23, .nr_max_vchans = 24, - .gate_needed = true, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23; }; static const struct of_device_id sun6i_dma_match[] = { @@ -1199,8 +1216,8 @@ static int sun6i_dma_probe(struct platform_device *pdev) goto err_dma_unregister; } - if (sdc->cfg->gate_needed) - writel(SUN8I_DMA_GATE_ENABLE, sdc->base + SUN8I_DMA_GATE); + if (sdc->cfg->clock_autogate_enable) + sdc->cfg->clock_autogate_enable(); return 0; -- 2.14.1
[PATCH v2 2/3] iio: adc: ina2xx: Use LSB specifier instead of divider in config
While the config uses the physical value corresponding to the LSB for both the power and the bus voltage register, the shunt voltage is specified as parts of 1 mV. Use the LSB physical value for all registers. No functional change. Signed-off-by: Stefan Brüns --- Changes in v2: - new patch drivers/iio/adc/ina2xx-adc.c | 12 ++-- 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 84094235ff7e..c59843de3999 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -115,7 +115,7 @@ enum ina2xx_ids { ina219, ina226 }; struct ina2xx_config { u16 config_default; int calibration_factor; - int shunt_div; + int shunt_voltage_lsb; /* nV */ int bus_voltage_shift; /* position of lsb */ int bus_voltage_lsb;/* uV */ int power_lsb; /* uW */ @@ -138,7 +138,7 @@ static const struct ina2xx_config ina2xx_config[] = { [ina219] = { .config_default = INA219_CONFIG_DEFAULT, .calibration_factor = 4096, - .shunt_div = 100, + .shunt_voltage_lsb = 1, .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT, .bus_voltage_lsb = 4000, .power_lsb = 2, @@ -147,7 +147,7 @@ static const struct ina2xx_config ina2xx_config[] = { [ina226] = { .config_default = INA226_CONFIG_DEFAULT, .calibration_factor = 512, - .shunt_div = 400, + .shunt_voltage_lsb = 2500, .bus_voltage_shift = 0, .bus_voltage_lsb = 1250, .power_lsb = 25000, @@ -204,9 +204,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->address) { case INA2XX_SHUNT_VOLTAGE: - /* processed (mV) = raw/shunt_div */ - *val2 = chip->config->shunt_div; - *val = 1; + /* processed (mV) = raw * lsb(nV) / 100 */ + *val = chip->config->shunt_voltage_lsb; + *val2 = 100; return IIO_VAL_FRACTIONAL; case INA2XX_BUS_VOLTAGE: -- 2.14.3
[PATCH v2 0/3] iio: adc: ina2xx: Fixes and extensions
The first patch fixes an issue with the conversion-ready status flag showing up in the bus voltage raw value. (removed original second patch from series, as already applied to togreg branch) The second patch cleans up the specification of the LSB value for the shunt voltage register. For code readability and uniformity, use the value from the datasheet (e.g 10uV per LSB) instead of the reciprocal (100 LSBs per mV), as done for the other registers. The third patch adds support for the INA219/220 specific bus voltage range and shunt voltage PGA. Changes in v2: - Apply to the shunt voltage, not bus voltage register - Shift instead of masking the LSBs - new patch - Fix conversion for shunt gain, function expects milli, not micro Stefan Brüns (3): iio: adc: ina2xx: Shift bus voltage register to mask flag bits iio: adc: ina2xx: Use LSB specifier instead of divider in config iio: adc: ina2xx: Allow setting Shunt Voltage PGA gain and Bus Voltage range drivers/iio/adc/ina2xx-adc.c | 149 +-- 1 file changed, 131 insertions(+), 18 deletions(-) -- 2.14.3
[PATCH v2 3/3] iio: adc: ina2xx: Allow setting Shunt Voltage PGA gain and Bus Voltage range
Reducing shunt and bus voltage range improves the accuracy, so allow altering the default settings. Both settings are exposed as gain values. While for the shunt voltage this is straightforward, the bus range settings of 32V (default) and 16V are mapped to gain values of 1 resp. 2, to provide a uniform API to userspace. As the gain settings are incorporated into the raw values by the sensor itself, adjusting of the scale attributes is not necessary. Signed-off-by: Stefan Brüns --- Changes in v2: - Fix conversion for shunt gain, function expects milli, not micro drivers/iio/adc/ina2xx-adc.c | 111 +-- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index c59843de3999..d05cb1b8efe6 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -48,8 +48,10 @@ #define INA2XX_MAX_REGISTERS8 /* settings - depend on use case */ -#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */ #define INA219_DEFAULT_IT 532 +#define INA219_DEFAULT_BRNG 1 /* 32V */ +#define INA219_DEFAULT_PGA 125 /* 1000/8 */ #define INA226_CONFIG_DEFAULT 0x4327 #define INA226_DEFAULT_AVG 4 #define INA226_DEFAULT_IT 1110 @@ -62,6 +64,14 @@ */ #define INA2XX_MODE_MASK GENMASK(3, 0) +/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */ +#define INA219_PGA_MASKGENMASK(12, 11) +#define INA219_SHIFT_PGA(val) ((val) << 11) + +/* VBus range: 32V (default), 16V */ +#define INA219_BRNG_MASK BIT(13) +#define INA219_SHIFT_BRNG(val) ((val) << 13) + /* Averaging for VBus/VShunt/Power */ #define INA226_AVG_MASKGENMASK(11, 9) #define INA226_SHIFT_AVG(val) ((val) << 9) @@ -131,6 +141,8 @@ struct ina2xx_chip_info { int avg; int int_time_vbus; /* Bus voltage integration time uS */ int int_time_vshunt; /* Shunt voltage integration time uS */ + int range_vbus; /* Bus voltage maximum in V */ + int pga_gain_vshunt; /* Shunt voltage PGA gain */ bool allow_async_readout; }; @@ -226,6 +238,18 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, *val = 1; return IIO_VAL_INT; } + + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *val = chip->pga_gain_vshunt; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_BUS_VOLTAGE: + *val = chip->range_vbus == 32 ? 1 : 2; + return IIO_VAL_INT; + } } return -EINVAL; @@ -360,6 +384,74 @@ static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, return 0; } +static const int ina219_vbus_range_tab[] = { 1, 2 }; +static int ina219_set_vbus_range_denom(struct ina2xx_chip_info *chip, + unsigned int range, + unsigned int *config) +{ + if (range == 1) + chip->range_vbus = 32; + else if (range == 2) + chip->range_vbus = 16; + else + return -EINVAL; + + *config &= ~INA219_BRNG_MASK; + *config |= INA219_SHIFT_BRNG(range == 1 ? 1 : 0) & INA219_BRNG_MASK; + + return 0; +} + +static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 }; +static const int ina219_vshunt_gain_frac[] = { + 125, 1000, 250, 1000, 500, 1000, 1000, 1000 }; + +static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip, + unsigned int gain, + unsigned int *config) +{ + int bits; + + if (gain < 125 || gain > 1000) + return -EINVAL; + + bits = find_closest(gain, ina219_vshunt_gain_tab, + ARRAY_SIZE(ina219_vshunt_gain_tab)); + + chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits]; + bits = 3 - bits; + + *config &= ~INA219_PGA_MASK; + *config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK; + + return 0; +} + +static int ina2xx_read_avail(struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +const int **vals, int *type, int *length, +long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *type = IIO_VAL_FRACTIONAL; + *length = sizeof(ina219_vshunt_gain_frac) / sizeof(int); + *vals = ina219_vs
[PATCH v2 1/3] iio: adc: ina2xx: Shift bus voltage register to mask flag bits
Lower bits of the INA219/220 bus voltage register are conversion status flags, properly shift the value. When reading via IIO buffer, the value is passed on unaltered, shifting is the responsibility of the user. Signed-off-by: Stefan Brüns --- Changes in v2: - Apply to the shunt voltage, not bus voltage register - Shift instead of masking the LSBs drivers/iio/adc/ina2xx-adc.c | 26 +- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 3aff9556678f..84094235ff7e 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -44,7 +44,6 @@ #define INA226_MASK_ENABLE 0x06 #define INA226_CVRFBIT(3) -#define INA219_CNVRBIT(1) #define INA2XX_MAX_REGISTERS8 @@ -79,6 +78,11 @@ #define INA226_ITS_MASKGENMASK(5, 3) #define INA226_SHIFT_ITS(val) ((val) << 3) +/* INA219 Bus voltage register, low bits are flags */ +#define INA219_OVF BIT(0) +#define INA219_CNVRBIT(1) +#define INA219_BUS_VOLTAGE_SHIFT 3 + /* Cosmetic macro giving the sampling period for a full P=UxI cycle */ #define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \ * c->avg) @@ -112,7 +116,7 @@ struct ina2xx_config { u16 config_default; int calibration_factor; int shunt_div; - int bus_voltage_shift; + int bus_voltage_shift; /* position of lsb */ int bus_voltage_lsb;/* uV */ int power_lsb; /* uW */ enum ina2xx_ids chip_id; @@ -135,7 +139,7 @@ static const struct ina2xx_config ina2xx_config[] = { .config_default = INA219_CONFIG_DEFAULT, .calibration_factor = 4096, .shunt_div = 100, - .bus_voltage_shift = 3, + .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT, .bus_voltage_lsb = 4000, .power_lsb = 2, .chip_id = ina219, @@ -170,6 +174,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, else *val = regval; + if (chan->address == INA2XX_BUS_VOLTAGE) + *val >>= chip->config->bus_voltage_shift; + return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -203,9 +210,9 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, return IIO_VAL_FRACTIONAL; case INA2XX_BUS_VOLTAGE: - /* processed (mV) = raw*lsb (uV) / (1000 << shift) */ + /* processed (mV) = raw * lsb (uV) / 1000 */ *val = chip->config->bus_voltage_lsb; - *val2 = 1000 << chip->config->bus_voltage_shift; + *val2 = 1000; return IIO_VAL_FRACTIONAL; case INA2XX_POWER: @@ -532,7 +539,7 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, * Sampling Freq is a consequence of the integration times of * the Voltage channels. */ -#define INA219_CHAN_VOLTAGE(_index, _address) { \ +#define INA219_CHAN_VOLTAGE(_index, _address, _shift) { \ .type = IIO_VOLTAGE, \ .address = (_address), \ .indexed = 1, \ @@ -544,7 +551,8 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, .scan_index = (_index), \ .scan_type = { \ .sign = 'u', \ - .realbits = 16, \ + .shift = _shift, \ + .realbits = 16 - _shift, \ .storagebits = 16, \ .endianness = IIO_LE, \ } \ @@ -579,8 +587,8 @@ static const struct iio_chan_spec ina226_channels[] = { }; static const struct iio_chan_spec ina219_channels[] = { - INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE), - INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE), + INA219_CHAN_VOLTAGE(0, INA2XX_SHUNT_VOLTAGE, 0), + INA219_CHAN_VOLTAGE(1, INA2XX_BUS_VOLTAGE, INA219_BUS_VOLTAGE_SHIFT), INA219_CHAN(IIO_POWER, 2, INA2XX_POWER), INA219_CHAN(IIO_CURRENT, 3, INA2XX_CURRENT), IIO_CHAN_SOFT_TIMESTAMP(4), -- 2.14.3
[PATCH] platform/x86: hp-wmi: Fix tablet mode detection for convertibles
Commit f9cf3b2880cc ("platform/x86: hp-wmi: Refactor dock and tablet state fetchers") consolidated the methods for docking and laptop mode detection, but omitted to apply the correct mask for the laptop mode (it always uses the constant for docking). Signed-off-by: Stefan Brüns --- This change is untested, but restores the previous behaviour. --- drivers/platform/x86/hp-wmi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/platform/x86/hp-wmi.c b/drivers/platform/x86/hp-wmi.c index b4ed3dc983d5..b4224389febe 100644 --- a/drivers/platform/x86/hp-wmi.c +++ b/drivers/platform/x86/hp-wmi.c @@ -297,7 +297,7 @@ static int hp_wmi_hw_state(int mask) if (state < 0) return state; - return state & 0x1; + return !!(state & mask); } static int __init hp_wmi_bios_2008_later(void) -- 2.14.3
[PATCH 4/4] platform/x86: intel-vbtn: support panel front button
The Lenovo Helix 2 and Dell XPS 12 (9Q33) have an extra button on the front showing a 'Windows' logo, both reporting event codes 0xC2/0xC3 on press/release. Tested on XPS 12, for info on the Helix 2 see: https://www.spinics.net/lists/ibm-acpi-devel/msg03982.html Signed-off-by: Stefan Brüns --- drivers/platform/x86/intel-vbtn.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index 89d395b8e36d..4e074b2ee127 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -38,6 +38,8 @@ static const struct acpi_device_id intel_vbtn_ids[] = { static const struct key_entry intel_vbtn_keymap[] = { { KE_KEY, 0xC0, { KEY_POWER } },/* power key press */ { KE_IGNORE, 0xC1, { KEY_POWER } }, /* power key release */ + { KE_KEY, 0xC2, { KEY_MENU } }, /* 'Windows' key press */ + { KE_IGNORE, 0xC3, { KEY_MENU } }, /* 'Windows' key release */ { KE_KEY, 0xC4, { KEY_VOLUMEUP } }, /* volume-up key press */ { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */ -- 2.14.3
[PATCH 2/4] platform/x86: intel-vbtn: support SW_TABLET_MODE
Event code 0xcc is emitted by several convertibles (Dell XPS 12 9Q33 BIOS A8, Dell XPS 13 2in1 9365, HP Spectre x360, Lenovo Thinkpad Helix) when entering tablet mode, and 0xcd on return to laptop mode. Signed-off-by: Stefan Brüns --- The changes where tested on an XPS 12 with BIOS version A8 (2015-03-03). An earlier BIOS version (A2, ~2013) did not report event, at least not using the INT33D6 plattform device. There are other convertible laptops apparenly using the same event codes: https://wiki.gentoo.org/wiki/HP_Spectre_x360_(2015) https://bugs.launchpad.net/ubuntu/+source/unity8/+bug/1678200 https://forums.opensuse.org/showthread.php/526850-Touchpad-and-trackpoint-no-longer-working-after-reattaching-convertible-keyboard --- drivers/platform/x86/intel-vbtn.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index 58c5ff36523a..e77b9143f9b3 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -42,6 +42,8 @@ static const struct key_entry intel_vbtn_keymap[] = { { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },/* volume-down key release */ + { KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */ + { KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } }, /* Laptop */ { KE_END }, }; -- 2.14.3
[PATCH 3/4] platform/x86: intel-vbtn: support KEY_ROTATE_DISPLAY
The Rotate Lock button event is emitted on the XPS 12 (BIOS A8, but not on BIOS A2). Signed-off-by: Stefan Brüns --- The changes where tested on an XPS 12 with BIOS version A8 (2015-03-03). An earlier BIOS version (A2, ~2013) did not report the event, at least not using the INT33D6 plattform device. --- drivers/platform/x86/intel-vbtn.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/platform/x86/intel-vbtn.c b/drivers/platform/x86/intel-vbtn.c index e77b9143f9b3..89d395b8e36d 100644 --- a/drivers/platform/x86/intel-vbtn.c +++ b/drivers/platform/x86/intel-vbtn.c @@ -42,6 +42,8 @@ static const struct key_entry intel_vbtn_keymap[] = { { KE_IGNORE, 0xC5, { KEY_VOLUMEUP } }, /* volume-up key release */ { KE_KEY, 0xC6, { KEY_VOLUMEDOWN } }, /* volume-down key press */ { KE_IGNORE, 0xC7, { KEY_VOLUMEDOWN } },/* volume-down key release */ + { KE_KEY, 0xC8, { KEY_ROTATE_DISPLAY } }, /* rotate-lock key press */ + { KE_IGNORE, 0xC9, { KEY_ROTATE_DISPLAY } },/* rotate-lock key release */ { KE_SW, 0xCC, { .sw = { SW_TABLET_MODE, 1 } } }, /* Tablet */ { KE_SW, 0xCD, { .sw = { SW_TABLET_MODE, 0 } } }, /* Laptop */ { KE_END }, -- 2.14.3
[PATCH 0/4] platform/x86: intel-vbtn: Add support for several more switches/buttons
Currently, intel-vbtn only supports the most relevant buttons, although there are several more events, mostly in use by convertibles. This series adds support for three more events. One of these events is a switch (SW_*) event, which is currently not working when using sparse keymaps. The first patch fixes this combination. The second patch adds support for the SW_TABLET_MODE switch, which is used by current convertibles. The third patch adds support for the KEY_ROTATE_DISPLAY. On the Dell XPS 12 (9Q33), rotation lock is implemented as a button to toggle between locked and unlocked state. In locked state, the accelerometer should be ignored, while in unlocked the screen contents should autorotate based on the tablet orientation. The same functionality is likely implemented as a switch (SW_ROTATE_LOCK event) on different hardware. The fourth patch adds support for the "Windows logo" button/key found on the XPS 12 display (i.e. in tablet mode, it is the only key reachable). The Lenovo Helix 2 has an equivalent touch button. The event currently uses KEY_MENU, although a distinct key code may be a better choice. Stefan Brüns (4): Input: sparse-keymap - send sync event for KE_SW/KW_VSW platform/x86: intel-vbtn: support SW_TABLET_MODE platform/x86: intel-vbtn: support KEY_ROTATE_DISPLAY platform/x86: intel-vbtn: support panel front button drivers/input/sparse-keymap.c | 1 + drivers/platform/x86/intel-vbtn.c | 6 ++ 2 files changed, 7 insertions(+) -- 2.14.3
[PATCH 1/4] Input: sparse-keymap - send sync event for KE_SW/KW_VSW
Sync events are sent by sparse_keymap_report_entry for normal KEY_* events, and are generated by several drivers after generating SW_* events, so sparse_keymap_report_entry should do the same. Without the sync, events are accumulated in the kernel. Currently, no driver uses sparse-keymap for SW_* events, but it is required for the intel-vbtn platform driver to generate SW_TABLET_MODE events. Signed-off-by: Stefan Brüns --- drivers/input/sparse-keymap.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c index bb0349fa64bc..fd03e55768c9 100644 --- a/drivers/input/sparse-keymap.c +++ b/drivers/input/sparse-keymap.c @@ -255,6 +255,7 @@ void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *k case KE_VSW: input_report_switch(dev, ke->sw.code, value); + input_sync(dev); break; } } -- 2.14.3
Re: [2/3] iio: adc: ina2xx: Adhere to documented ABI, use Ohm instead of uOhm
On Thursday, November 2, 2017 10:04:01 AM CET Maciej Purski wrote: > On 10/14/2017 08:27 PM, Stefan Bruens wrote: > > On Montag, 9. Oktober 2017 11:29:43 CEST Maciej Purski wrote: > >> On 10/01/2017 09:48 PM, Stefan Brüns wrote: > >>> According to the ABI documentation, the shunt resistor value should be > >>> specificied in Ohm. As this is also used/documented for the MAX9611, > >>> use the same for the INA2xx driver. > >>> > >>> This poses an ABI break for anyone actually altering the shunt value > >>> through the sysfs interface, it does not alter the default value nor > >>> a value set from the devicetree. > >>> > >>> Minor change: Fix comment, 1mA is 10^-3A. > >> > >> I have just a minor issue. There could be an inconsistency with units as > >> in > >> my patch I make current_lsb adjustable and I need it to be in uA (it used > >> to be hardcoded as 1 mA so to achieve better precision we need smaller > >> units). So in order to keep calibration register properly scaled, I > >> convert > >> uOhms to mOhms on each set_calibration(). So if both my changes and your > >> changes were applied, on each shunt_resistore_store we would be > >> performing > >> multiplication by 10^6 and then in set_calibration() division by 10^3 > >> which > >> seems odd to me. > >> > >> I guess we could keep it as shunt_resistor_ohms instead of > >> shunt_resistor_uohm. We could avoid performing division on each > >> shunt_resistor_show() and perform multiplication by 10^3 only once in > >> set_calibration() on each > >> shunt_resistore_store(). We could then change the default value and > >> perform > >> division only on probing, when reading the shunt_resistance from device > >> tree. > >> > >> There are many other options. It's not a major issue so maybe we could > >> leave it as it is or you could suggest some changes in my patch. > > > > Sorry it took me so long to answer ... > > > > The current fixed current_lsb of 1mA is indeed a bad choice for everything > > but a shunt resistor value of 10mOhm, as it truncates the current value. > > So what is a *good* choice? > > > > One important point is the current register is merely more than a > > convenience register. At least for the INA219/220, it provides nothing > > not achievable in software, and for the INA226 family it only has added > > value if the current is varying faster than the readout frequency and the > > averaging is used. > > > > The precision of the current register is limited by the precision of the > > shunt voltage register, and may be reduced by the applied > > scaling/calibration factor. > > > > The precision of the shunt voltage register is fixed at 10uV (INA219) > > resp. > > 2.5uV (INA226). Changing conversion time (both) and PGA (219) affects the > > noise and offset, but the lsb value is still fixed. > > > > If one wants to carry over the shunt voltage register precision into the > > current register, its important no (or hardly any) truncation happens. The > > terms therefor are given in the manual, formulas 8.5.1 (4) resp 7.5.1 (3): > > > > INA219: current = shunt_voltage * cal_register / 4096 > > INA226: current = shunt_voltage * cal_register / 2048 > > > > So any cal value smaller than 4096 (2048) will introduce truncation > > errors, > > larger values may introduce overflows, if the full input range is used. > > Now, would it not be wise to always use 4096 (2048) for the calibration > > value? > > > > The raw values from the IIO subsystem are meaningless without their > > accompanying scale factor. Instead of changing the calibration value, why > > not just change the reported scale factor? > > > > More opinions are very welcome. > > > > Kind regards, > > > > Stefan > > Thanks for the reply. > > I agree that cal_register set to 4096 (2048) allows us to eliminate > truncaction error. However according to your suggestion, if we made cal_reg > a fixed value, then current_lsb and r_shunt should be also a fixed value, > as they are related according to formula 8.5 (1) > > cal_register = 0.00512 / (current_lsb * r_shunt) A fixed cal_register only means the current_lsb is implied by the selected shunt resistor value. If you insert 2048 into the equation above, you get: current_lsb = 2.5 * 1e-6 * r_shunt, and using Ohms law to replace r_shunt, thats exactly the resolution of the shunt_voltag
[RESEND PATCH 0/2] Enable SPI on A64/Pine64
The A64 SPI controller is compatible to the H3/H5 controller, i.e. same registers and same queue depth. The Pine64 exposes both controllers on the PI-2 and Euler connectors. Tested/verified with logic analyser and spidev_test using MOSI/MISO loopback. Note: spi0 on the A64 may conflict with the eMMC controller, as the MISO pin is also used for the HS400 eMMC data strobe. This is only a concern if the board uses eMMC (does not apply to the Pine64) *and* is using HS400 mode. The same pin conflict exists for the H5. Stefan Brüns (2): arm64: allwinner: a64: add SPI nodes arm64: allwinner: pine64: Enable spi0/spi1 .../arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 8 + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 40 ++ 2 files changed, 48 insertions(+) -- 2.14.1
[RESEND PATCH 1/2] arm64: allwinner: a64: add SPI nodes
The A64 SPI controllers are register compatible to the h3/h5 SPI controllers. The A64 has two SPI controllers, each with a single chip select. The handles for the DMA channels (23/24 for SPI0/SPI1) are omitted, as the A64 DMA controller node is currently missing. Signed-off-by: Stefan Brüns --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 40 +++ 1 file changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index bd0f33b77f57..373cd14f0206 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -325,6 +325,16 @@ drive-strength = <40>; }; + spi0_pins: spi0 { + pins = "PC0", "PC1", "PC2", "PC3"; + function = "spi0"; + }; + + spi1_pins: spi1 { + pins = "PD0", "PD1", "PD2", "PD3"; + function = "spi1"; + }; + uart0_pins_a: uart0@0 { pins = "PB8", "PB9"; function = "uart0"; @@ -527,5 +537,35 @@ #address-cells = <1>; #size-cells = <0>; }; + + spi0: spi@01c68000 { + compatible = "allwinner,sun8i-h3-spi"; + reg = <0x01c68000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; + clock-names = "ahb", "mod"; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; + resets = <&ccu RST_BUS_SPI0>; + status = "disabled"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + spi1: spi@01c69000 { + compatible = "allwinner,sun8i-h3-spi"; + reg = <0x01c69000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; + clock-names = "ahb", "mod"; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins>; + resets = <&ccu RST_BUS_SPI1>; + status = "disabled"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; }; }; -- 2.14.1
[RESEND PATCH 2/2] arm64: allwinner: pine64: Enable spi0/spi1
The two spi channels/controllers are available on the PI-2 resp. Euler connector, enable both. Contrary to the Pi, the A64 SOC only supports one chip select, so the second chipselect is not available (though it can be emulated using gpio chipselect). Signed-off-by: Stefan Brüns --- arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts | 8 1 file changed, 8 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts index 827168bc22ed..f25274e8b3e0 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64-pine64.dts @@ -124,6 +124,14 @@ status = "okay"; }; +&spi0 { + status = "okay"; +}; + +&spi1 { + status = "okay"; +}; + /* On Exp and Euler connectors */ &uart0 { pinctrl-names = "default"; -- 2.14.1
[PATCH v2] arm64: allwinner: a64: add SPI nodes
The A64 SPI controllers are register compatible to the h3/h5 SPI controllers. The A64 has two SPI controllers, each with a single chip select. The handles for the DMA channels (23/24 for SPI0/SPI1) are omitted, as the A64 DMA support is currently missing. Signed-off-by: Stefan Brüns --- v2: - order nodes by memory address - drop enabling of spi nodes for Pine64, spi pins are on external connector and some other function might be used arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 40 +++ 1 file changed, 40 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index bd0f33b77f57..7be9eb2ad83c 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -325,6 +325,16 @@ drive-strength = <40>; }; + spi0_pins: spi0 { + pins = "PC0", "PC1", "PC2", "PC3"; + function = "spi0"; + }; + + spi1_pins: spi1 { + pins = "PD0", "PD1", "PD2", "PD3"; + function = "spi1"; + }; + uart0_pins_a: uart0@0 { pins = "PB8", "PB9"; function = "uart0"; @@ -469,6 +479,36 @@ }; }; + spi0: spi@01c68000 { + compatible = "allwinner,sun8i-h3-spi"; + reg = <0x01c68000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; + clock-names = "ahb", "mod"; + pinctrl-names = "default"; + pinctrl-0 = <&spi0_pins>; + resets = <&ccu RST_BUS_SPI0>; + status = "disabled"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + + spi1: spi@01c69000 { + compatible = "allwinner,sun8i-h3-spi"; + reg = <0x01c69000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; + clock-names = "ahb", "mod"; + pinctrl-names = "default"; + pinctrl-0 = <&spi1_pins>; + resets = <&ccu RST_BUS_SPI1>; + status = "disabled"; + num-cs = <1>; + #address-cells = <1>; + #size-cells = <0>; + }; + gic: interrupt-controller@1c81000 { compatible = "arm,gic-400"; reg = <0x01c81000 0x1000>, -- 2.14.1
[PATCH 2/3] arm64: allwinner: a64: Add device node for DMA controller
The A64 SoC has a DMA controller that supports 8 DMA channels to and from various peripherals. Add a device node for it. Signed-off-by: Stefan Brüns --- Documentation/devicetree/bindings/dma/sun6i-dma.txt | 1 + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 9 + 2 files changed, 10 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt index 6b267045f522..4398b990250c 100644 --- a/Documentation/devicetree/bindings/dma/sun6i-dma.txt +++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt @@ -9,6 +9,7 @@ Required properties: "allwinner,sun8i-a23-dma" "allwinner,sun8i-a83t-dma" "allwinner,sun8i-h3-dma" + "allwinner,sun50i-a64-dma" - reg: Should contain the registers base address and length - interrupts: Should contain a reference to the interrupt used by this device - clocks: Should contain a reference to the parent AHB clock diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index 7be9eb2ad83c..f96287d3043a 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -136,6 +136,15 @@ reg = <0x01c0 0x1000>; }; + dma: dma-controller@01c02000 { + compatible = "allwinner,sun50i-a64-dma"; + reg = <0x01c02000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_DMA>; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + }; + mmc0: mmc@1c0f000 { compatible = "allwinner,sun50i-a64-mmc"; reg = <0x01c0f000 0x1000>; -- 2.14.1
[PATCH 0/3] dmaengine: Fix DMA on current allwinner SoCs, add A64 support
The latest generation of allwinner SoCs (e.g. H3, H5, A64) have some significant differences to older SoC generations (e.g. A23): - different register location for the clock autogating register (offset 0x28 vs 0x20) - different field offset for the src/dest burst length fields in the channel configuration register - additional burst lengths and widths This is the second change to the implementation of the DMA controller (first change being the addition of the clock autogating register in the A23). To differentiate between the different generations, the patch adds an enum value to the OF config data. The A83t uses the same register layout as the A23, so this series also fixes the missing clock gating setting for the A83t. The A64 is register compatible to the H3, but supports less channels and has a different set of endpoints. Add a new compatible string to the device tree and the driver. Stefan Brüns (3): dmaengine: sun6i: Correct DMA support on H3 arm64: allwinner: a64: Add device node for DMA controller dmaengine: sun6i: Add support for Allwinner A64 .../devicetree/bindings/dma/sun6i-dma.txt | 1 + arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 13 ++ drivers/dma/sun6i-dma.c| 135 +++-- 3 files changed, 109 insertions(+), 40 deletions(-) -- 2.14.1
[PATCH 3/3] dmaengine: sun6i: Add support for Allwinner A64
The A64 SoC has the same dma engine as the H3 (sun8i), with a reduced amount of physical channels. Add the proper config data and compatible string to support it. Signed-off-by: Stefan Brüns --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 4 drivers/dma/sun6i-dma.c | 7 +++ 2 files changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index f96287d3043a..b86019238b77 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -494,6 +494,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; clock-names = "ahb", "mod"; + dmas = <&dma 23>, <&dma 23>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi0_pins>; resets = <&ccu RST_BUS_SPI0>; @@ -509,6 +511,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; clock-names = "ahb", "mod"; + dmas = <&dma 24>, <&dma 24>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi1_pins>; resets = <&ccu RST_BUS_SPI1>; diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 5f4eee4513e5..6a17c5d63582 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -1068,6 +1068,12 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_vchans = 34, .dmac_variant= DMAC_VARIANT_H3, }; + +static struct sun6i_dma_config sun50i_a64_dma_cfg = { + .nr_max_channels = 8, + .nr_max_requests = 27, + .nr_max_vchans = 38, + .dmac_variant= DMAC_VARIANT_H3, }; static const struct of_device_id sun6i_dma_match[] = { @@ -1075,6 +1081,7 @@ static const struct of_device_id sun6i_dma_match[] = { { .compatible = "allwinner,sun8i-a23-dma", .data = &sun8i_a23_dma_cfg }, { .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg }, { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg }, + { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun6i_dma_match); -- 2.14.1
[PATCH 1/3] dmaengine: sun6i: Correct DMA support on H3
H3 (and A64/H5) have a sligthly different DMA controller compared with older SoC generations: - it supports a buswidth of 8 bytes - it supports burst length of 4 and 16 transfers - the register offset for the burst lengths are different, it uses bits [6:7]/[22:23] instead of [7:8]/[23:24] for the src/dest lengths. Set the src_addr_widths/dest_addr_widths fields in struct dma_device according to the supported widths and use these for verification of the slave configuration. As struct dma_device has no detailed information for supported burst lengths (only maxburst), the information is added to the config. Separating verification of the config and conversion to register values allows to keep both independent of the used controller. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 128 +--- 1 file changed, 88 insertions(+), 40 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index a2358780ab2c..5f4eee4513e5 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -48,6 +48,9 @@ #define SUN8I_DMA_GATE 0x20 #define SUN8I_DMA_GATE_ENABLE 0x4 +#define SUNXI_H3_SECURITE_REG 0x20 +#define SUNXI_H3_DMA_GATE 0x28 +#define SUNXI_H3_DMA_GATE_ENABLE 0x4 /* * Channels specific registers */ @@ -65,13 +68,15 @@ #define DMA_CHAN_CFG_SRC_DRQ(x)((x) & 0x1f) #define DMA_CHAN_CFG_SRC_IO_MODE BIT(5) #define DMA_CHAN_CFG_SRC_LINEAR_MODE (0 << 5) -#define DMA_CHAN_CFG_SRC_BURST(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_A31(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_H3(x) (((x) & 0x3) << 6) #define DMA_CHAN_CFG_SRC_WIDTH(x) (((x) & 0x3) << 9) #define DMA_CHAN_CFG_DST_DRQ(x)(DMA_CHAN_CFG_SRC_DRQ(x) << 16) #define DMA_CHAN_CFG_DST_IO_MODE (DMA_CHAN_CFG_SRC_IO_MODE << 16) #define DMA_CHAN_CFG_DST_LINEAR_MODE (DMA_CHAN_CFG_SRC_LINEAR_MODE << 16) -#define DMA_CHAN_CFG_DST_BURST(x) (DMA_CHAN_CFG_SRC_BURST(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_A31(x) (DMA_CHAN_CFG_SRC_BURST_A31(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_H3(x) (DMA_CHAN_CFG_SRC_BURST_H3(x) << 16) #define DMA_CHAN_CFG_DST_WIDTH(x) (DMA_CHAN_CFG_SRC_WIDTH(x) << 16) #define DMA_CHAN_CUR_SRC 0x10 @@ -90,6 +95,16 @@ #define NORMAL_WAIT8 #define DRQ_SDRAM 1 +/* Between SoC generations, there are some significant differences: + * - A23 added a clock gate register + * - the H3 burst length field has a different offset + */ +enum dmac_variant { + DMAC_VARIANT_A31, + DMAC_VARIANT_A23, + DMAC_VARIANT_H3, +}; + /* * Hardware channels / ports representation * @@ -101,6 +116,7 @@ struct sun6i_dma_config { u32 nr_max_channels; u32 nr_max_requests; u32 nr_max_vchans; + enum dmac_variant dmac_variant; }; /* @@ -240,8 +256,12 @@ static inline s8 convert_burst(u32 maxburst) switch (maxburst) { case 1: return 0; + case 4: + return 1; case 8: return 2; + case 16: + return 3; default: return -EINVAL; } @@ -249,11 +269,7 @@ static inline s8 convert_burst(u32 maxburst) static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { - if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) || - (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) - return -EINVAL; - - return addr_width >> 1; + return ilog2(addr_width); } static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) @@ -499,45 +515,58 @@ static int set_config(struct sun6i_dma_dev *sdev, enum dma_transfer_direction direction, u32 *p_cfg) { + enum dma_slave_buswidth src_addr_width, dst_addr_width; + u32 src_maxburst, dst_maxburst, supported_burst_length; s8 src_width, dst_width, src_burst, dst_burst; + src_addr_width = sconfig->src_addr_width; + dst_addr_width = sconfig->dst_addr_width; + src_maxburst = sconfig->src_maxburst; + dst_maxburst = sconfig->dst_maxburst; + + if (sdev->cfg->dmac_variant == DMAC_VARIANT_H3) + supported_burst_length = BIT(1) | BIT(4) | BIT(8) | BIT(16); + else + supported_burst_length = BIT(1) | BIT(8); + switch (direction) { case DMA_MEM_TO_DEV: - src_burst = convert_burst(sconfig->src_maxburst ? - sconfig->src_maxburst : 8); - src_width = convert_buswidth(sconfig->src_addr_width != - DMA_SLAVE_BUSWIDTH_UNDEFINED ? - sconfig->src_addr_width : - DMA_SLAVE_BUS
[PATCH v4 08/11] dmaengine: sun6i: Add support for Allwinner A64 and compatibles
The A64 SoC has the same dma engine as the H3 (sun8i), with a reduced amount of physical channels. To allow future reuse of the compatible, leave the channel count etc. in the config data blank and retrieve it from the devicetree. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v4: - Fix config initialization Changes in v3: - Omit default values from sun50i_a64_dma_cfg definition Changes in v2: None drivers/dma/sun6i-dma.c | 20 1 file changed, 20 insertions(+) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index b5906da2a975..945436e477d7 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -1125,6 +1125,25 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), }; +/* + * The A64 binding uses the number of dma channels from the + * device tree node. + */ +static struct sun6i_dma_config sun50i_a64_dma_cfg = { + .clock_autogate_enable = sun6i_enable_clock_autogate_h3, + .set_burst_length = sun6i_set_burst_length_h3, + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16), + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16), + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), +}; + /* * The V3s have only 8 physical channels, a maximum DRQ port id of 23, * and a total of 24 usable source and destination endpoints. @@ -1152,6 +1171,7 @@ static const struct of_device_id sun6i_dma_match[] = { { .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg }, { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg }, { .compatible = "allwinner,sun8i-v3s-dma", .data = &sun8i_v3s_dma_cfg }, + { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun6i_dma_match); -- 2.14.1
[PATCH v4 06/11] arm64: allwinner: a64: Add devicetree binding for DMA controller
The A64 is register compatible with the H3, but has a different number of dma channels and request ports. Attach additional properties to the node to allow future reuse of the compatible for controllers with different number of channels/requests. If dma-requests is not specified, the register layout defined maximum of 32 is used. Signed-off-by: Stefan Brüns Acked-by: Rob Herring --- Changes in v4: None Changes in v3: - Drop leading 0 from unit name in DT example Changes in v2: None .../devicetree/bindings/dma/sun6i-dma.txt | 26 ++ 1 file changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt index 98fbe1a5c6dd..9700b1d00fed 100644 --- a/Documentation/devicetree/bindings/dma/sun6i-dma.txt +++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt @@ -27,6 +27,32 @@ Example: #dma-cells = <1>; }; +-- +For A64 DMA controller: + +Required properties: +- compatible: "allwinner,sun50i-a64-dma" +- dma-channels: Number of DMA channels supported by the controller. + Refer to Documentation/devicetree/bindings/dma/dma.txt +- all properties above, i.e. reg, interrupts, clocks, resets and #dma-cells + +Optional properties: +- dma-requests: Number of DMA request signals supported by the controller. + Refer to Documentation/devicetree/bindings/dma/dma.txt + +Example: + dma: dma-controller@1c02000 { + compatible = "allwinner,sun50i-a64-dma"; + reg = <0x01c02000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_DMA>; + dma-channels = <8>; + dma-requests = <27>; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + }; +-- + Clients: DMA clients connected to the A31 DMA controller must use the format -- 2.14.1
[PATCH v4 10/11] arm64: allwinner: a64: add dma controller references to spi nodes
The spi controller nodes omit the dma controller/channel references, add it. This does not yet enable DMA for SPI transfers, as the spi-sun6i driver lacks support for DMA, but always uses PIO to the FIFO. Signed-off-by: Stefan Brüns --- Changes in v4: None Changes in v3: None Changes in v2: None arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 4 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index cbffefce0e71..a6a5a40d76d0 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -476,6 +476,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; clock-names = "ahb", "mod"; + dmas = <&dma 23>, <&dma 23>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi0_pins>; resets = <&ccu RST_BUS_SPI0>; @@ -491,6 +493,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; clock-names = "ahb", "mod"; + dmas = <&dma 24>, <&dma 24>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi1_pins>; resets = <&ccu RST_BUS_SPI1>; -- 2.14.1
[PATCH v4 11/11] arm: allwinner: Correct unit name in devicetree binding example
Unit-names must not start with a leading 0. Signed-off-by: Stefan Brüns --- Changes in v4: - Split minor fix in devicetree example from patch 6/10 Changes in v3: None Changes in v2: None Documentation/devicetree/bindings/dma/sun6i-dma.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt index 9700b1d00fed..b2df4f0f1488 100644 --- a/Documentation/devicetree/bindings/dma/sun6i-dma.txt +++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt @@ -18,7 +18,7 @@ Required properties: - #dma-cells : Should be 1, a single cell holding a line request number Example: - dma: dma-controller@01c02000 { + dma: dma-controller@1c02000 { compatible = "allwinner,sun6i-a31-dma"; reg = <0x01c02000 0x1000>; interrupts = <0 50 4>; -- 2.14.1
[PATCH v4 09/11] arm64: allwinner: a64: Add device node for DMA controller
The A64 SoC has a DMA controller that supports 8 DMA channels to and from various peripherals. The last used DRQ port is 27. Add a device node for it. Signed-off-by: Stefan Brüns --- Changes in v4: None Changes in v3: - Drop leading 0 from dma controller unit name Changes in v2: None arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 11 +++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index e9a9d7fb01c8..cbffefce0e71 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -136,6 +136,17 @@ reg = <0x01c0 0x1000>; }; + dma: dma-controller@1c02000 { + compatible = "allwinner,sun50i-a64-dma"; + reg = <0x01c02000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_DMA>; + dma-channels = <8>; + dma-requests = <27>; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + }; + mmc0: mmc@1c0f000 { compatible = "allwinner,sun50i-a64-mmc"; reg = <0x01c0f000 0x1000>; -- 2.14.1
[PATCH v4 04/11] dmaengine: sun6i: Enable additional burst lengths/widths on H3
The H3 supports bursts lengths of 1, 4, 8 and 16 transfers, each with a width of 1, 2, 4 or 8 bytes. The register value for the the width is log2-encoded, change the conversion function to provide the correct value for width == 8. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v4: - Fix config initialization Changes in v3: None Changes in v2: - Store burst widths in config drivers/dma/sun6i-dma.c | 54 - 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 24dc7fac7447..eb45ab39eddd 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -123,6 +123,8 @@ struct sun6i_dma_config { void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); u32 src_burst_lengths; u32 dst_burst_lengths; + u32 src_addr_widths; + u32 dst_addr_widths; }; /* @@ -262,8 +264,12 @@ static inline s8 convert_burst(u32 maxburst) switch (maxburst) { case 1: return 0; + case 4: + return 1; case 8: return 2; + case 16: + return 3; default: return -EINVAL; } @@ -271,7 +277,7 @@ static inline s8 convert_burst(u32 maxburst) static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { - return addr_width >> 1; + return ilog2(addr_width); } static void sun6i_enable_clock_autogate_a23(struct sun6i_dma_dev *sdev) @@ -1043,6 +1049,12 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31, .src_burst_lengths = BIT(1) | BIT(8), .dst_burst_lengths = BIT(1) | BIT(8), + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES), + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES), }; /* @@ -1058,6 +1070,12 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31, .src_burst_lengths = BIT(1) | BIT(8), .dst_burst_lengths = BIT(1) | BIT(8), + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES), + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES), }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { @@ -1068,11 +1086,19 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31, .src_burst_lengths = BIT(1) | BIT(8), .dst_burst_lengths = BIT(1) | BIT(8), + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES), + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES), }; /* * The H3 has 12 physical channels, a maximum DRQ port id of 27, * and a total of 34 usable source and destination endpoints. + * It also supports additional burst lengths and bus widths, + * and the burst length fields have different offsets. */ static struct sun6i_dma_config sun8i_h3_dma_cfg = { @@ -1081,8 +1107,16 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_vchans = 34, .clock_autogate_enable = sun6i_enable_clock_autogate_h3, .set_burst_length = sun6i_set_burst_length_h3, - .src_burst_lengths = BIT(1) | BIT(8), - .dst_burst_lengths = BIT(1) | BIT(8), + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16), + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16), + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES), }; /* @@ -1098,6 +1132,12 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31, .src_burst_lengths = BIT(1) | BIT(8), .dst_burst_lengths = BIT(1) | BIT(8), + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_
[PATCH v4 07/11] dmaengine: sun6i: Retrieve channel count/max request from devicetree
To avoid introduction of a new compatible for each small SoC/DMA controller variation, move the definition of the channel count to the devicetree. The number of vchans is no longer explicit, but limited by the highest port/DMA request number. The result is a slight overallocation for SoCs with a sparse port mapping. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v4: - remove range checks for dma-channels/dma-requests DT properties Changes in v3: None Changes in v2: - Set default number of dma-request if not provided in config or devicetree drivers/dma/sun6i-dma.c | 27 ++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 7fce976a13d8..b5906da2a975 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -42,6 +42,9 @@ #define DMA_STAT 0x30 +/* Offset between DMA_IRQ_EN and DMA_IRQ_STAT limits number of channels */ +#define DMA_MAX_CHANNELS (DMA_IRQ_CHAN_NR * 0x10 / 4) + /* * sun8i specific registers */ @@ -65,7 +68,8 @@ #define DMA_CHAN_LLI_ADDR 0x08 #define DMA_CHAN_CUR_CFG 0x0c -#define DMA_CHAN_CFG_SRC_DRQ(x)((x) & 0x1f) +#define DMA_CHAN_MAX_DRQ 0x1f +#define DMA_CHAN_CFG_SRC_DRQ(x)((x) & DMA_CHAN_MAX_DRQ) #define DMA_CHAN_CFG_SRC_IO_MODE BIT(5) #define DMA_CHAN_CFG_SRC_LINEAR_MODE (0 << 5) #define DMA_CHAN_CFG_SRC_BURST_A31(x) (((x) & 0x3) << 7) @@ -1155,6 +1159,7 @@ MODULE_DEVICE_TABLE(of, sun6i_dma_match); static int sun6i_dma_probe(struct platform_device *pdev) { const struct of_device_id *device; + struct device_node *np = pdev->dev.of_node; struct sun6i_dma_dev *sdc; struct resource *res; int ret, i; @@ -1230,6 +1235,26 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->num_vchans = sdc->cfg->nr_max_vchans; sdc->max_request = sdc->cfg->nr_max_requests; + ret = of_property_read_u32(np, "dma-channels", &sdc->num_pchans); + if (ret && !sdc->num_pchans) { + dev_err(&pdev->dev, "Can't get dma-channels.\n"); + return ret; + } + + ret = of_property_read_u32(np, "dma-requests", &sdc->max_request); + if (ret && !sdc->max_request) { + dev_info(&pdev->dev, "Missing dma-requests, using %u.\n", +DMA_CHAN_MAX_DRQ); + sdc->max_request = DMA_CHAN_MAX_DRQ; + } + + /* +* If the number of vchans is not specified, derive it from the +* highest port number, at most one channel per port and direction. +*/ + if (!sdc->num_vchans) + sdc->num_vchans = 2 * (sdc->max_request + 1); + sdc->pchans = devm_kcalloc(&pdev->dev, sdc->num_pchans, sizeof(struct sun6i_pchan), GFP_KERNEL); if (!sdc->pchans) -- 2.14.1
[PATCH v4 02/11] dmaengine: sun6i: Correct burst length field offsets for H3
For the H3, the burst lengths field offsets in the channel configuration register differs from earlier SoC generations. Using the A31 register macros actually configured the H3 controller do to bursts of length 1 always, which although working leads to higher bus utilisation. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v4: - Pass reference to config instead of config itself - Fix config initialization Changes in v3: None Changes in v2: - Use controller specific callback for burst length setting drivers/dma/sun6i-dma.c | 34 +++--- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 6f9d7c4ac7c0..48b3701c9aa1 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -68,13 +68,15 @@ #define DMA_CHAN_CFG_SRC_DRQ(x)((x) & 0x1f) #define DMA_CHAN_CFG_SRC_IO_MODE BIT(5) #define DMA_CHAN_CFG_SRC_LINEAR_MODE (0 << 5) -#define DMA_CHAN_CFG_SRC_BURST(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_A31(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_H3(x) (((x) & 0x3) << 6) #define DMA_CHAN_CFG_SRC_WIDTH(x) (((x) & 0x3) << 9) #define DMA_CHAN_CFG_DST_DRQ(x)(DMA_CHAN_CFG_SRC_DRQ(x) << 16) #define DMA_CHAN_CFG_DST_IO_MODE (DMA_CHAN_CFG_SRC_IO_MODE << 16) #define DMA_CHAN_CFG_DST_LINEAR_MODE (DMA_CHAN_CFG_SRC_LINEAR_MODE << 16) -#define DMA_CHAN_CFG_DST_BURST(x) (DMA_CHAN_CFG_SRC_BURST(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_A31(x) (DMA_CHAN_CFG_SRC_BURST_A31(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_H3(x) (DMA_CHAN_CFG_SRC_BURST_H3(x) << 16) #define DMA_CHAN_CFG_DST_WIDTH(x) (DMA_CHAN_CFG_SRC_WIDTH(x) << 16) #define DMA_CHAN_CUR_SRC 0x10 @@ -118,6 +120,7 @@ struct sun6i_dma_config { * BSP kernel source code. */ void (*clock_autogate_enable)(struct sun6i_dma_dev *); + void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); }; /* @@ -283,6 +286,18 @@ static void sun6i_enable_clock_autogate_h3(struct sun6i_dma_dev *sdev) writel(SUNXI_H3_DMA_GATE_ENABLE, sdev->base + SUNXI_H3_DMA_GATE); } +static void sun6i_set_burst_length_a31(u32 *p_cfg, s8 src_burst, s8 dst_burst) +{ + *p_cfg |= DMA_CHAN_CFG_SRC_BURST_A31(src_burst) | + DMA_CHAN_CFG_DST_BURST_A31(dst_burst); +} + +static void sun6i_set_burst_length_h3(u32 *p_cfg, s8 src_burst, s8 dst_burst) +{ + *p_cfg |= DMA_CHAN_CFG_SRC_BURST_H3(src_burst) | + DMA_CHAN_CFG_DST_BURST_H3(dst_burst); +} + static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) { struct sun6i_desc *txd = pchan->desc; @@ -562,11 +577,11 @@ static int set_config(struct sun6i_dma_dev *sdev, if (dst_width < 0) return dst_width; - *p_cfg = DMA_CHAN_CFG_SRC_BURST(src_burst) | - DMA_CHAN_CFG_SRC_WIDTH(src_width) | - DMA_CHAN_CFG_DST_BURST(dst_burst) | + *p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) | DMA_CHAN_CFG_DST_WIDTH(dst_width); + sdev->cfg->set_burst_length(p_cfg, src_burst, dst_burst); + return 0; } @@ -609,11 +624,11 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) | DMA_CHAN_CFG_DST_LINEAR_MODE | DMA_CHAN_CFG_SRC_LINEAR_MODE | - DMA_CHAN_CFG_SRC_BURST(burst) | DMA_CHAN_CFG_SRC_WIDTH(width) | - DMA_CHAN_CFG_DST_BURST(burst) | DMA_CHAN_CFG_DST_WIDTH(width); + sdev->cfg->set_burst_length(&v_lli->cfg, burst, burst); + sun6i_dma_lli_add(NULL, v_lli, p_lli, txd); sun6i_dma_dump_lli(vchan, v_lli); @@ -1025,6 +1040,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .nr_max_channels = 16, .nr_max_requests = 30, .nr_max_vchans = 53, + .set_burst_length = sun6i_set_burst_length_a31, }; /* @@ -1037,6 +1053,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_requests = 24, .nr_max_vchans = 37, .clock_autogate_enable = sun6i_enable_clock_autogate_a23, + .set_burst_length = sun6i_set_burst_length_a31, }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { @@ -1044,6 +1061,7 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .nr_max_requests = 28, .nr_max_vchans = 39, .clock_autogate_enable = sun6i_enable_clock_autogate_a23, + .set_burst_length = sun6i_set_burst_length_a31, }; /* @@ -1056,6 +1074,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_requests = 27, .nr_max_vchans = 34, .clock_autogate_enable = sun6i_enable_clock_autogate_h3, + .set_burst_length = sun6i_set_burst_lengt
[PATCH v4 03/11] dmaengine: sun6i: Restructure code to allow extension for new SoCs
The current code mixes three distinct operations when transforming the slave config to register settings: 1. special handling of DMA_SLAVE_BUSWIDTH_UNDEFINED, maxburst == 0 2. range checking 3. conversion of raw to register values As the range checks depend on the specific SoC, move these out of the conversion to distinct operations. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v4: - Fix config initialization Changes in v3: None Changes in v2: - Store burst lengths in config instead of device structure drivers/dma/sun6i-dma.c | 66 - 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 48b3701c9aa1..24dc7fac7447 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -121,6 +121,8 @@ struct sun6i_dma_config { */ void (*clock_autogate_enable)(struct sun6i_dma_dev *); void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); + u32 src_burst_lengths; + u32 dst_burst_lengths; }; /* @@ -269,10 +271,6 @@ static inline s8 convert_burst(u32 maxburst) static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { - if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) || - (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) - return -EINVAL; - return addr_width >> 1; } @@ -541,41 +539,43 @@ static int set_config(struct sun6i_dma_dev *sdev, enum dma_transfer_direction direction, u32 *p_cfg) { + enum dma_slave_buswidth src_addr_width, dst_addr_width; + u32 src_maxburst, dst_maxburst; s8 src_width, dst_width, src_burst, dst_burst; + src_addr_width = sconfig->src_addr_width; + dst_addr_width = sconfig->dst_addr_width; + src_maxburst = sconfig->src_maxburst; + dst_maxburst = sconfig->dst_maxburst; + switch (direction) { case DMA_MEM_TO_DEV: - src_burst = convert_burst(sconfig->src_maxburst ? - sconfig->src_maxburst : 8); - src_width = convert_buswidth(sconfig->src_addr_width != - DMA_SLAVE_BUSWIDTH_UNDEFINED ? - sconfig->src_addr_width : - DMA_SLAVE_BUSWIDTH_4_BYTES); - dst_burst = convert_burst(sconfig->dst_maxburst); - dst_width = convert_buswidth(sconfig->dst_addr_width); + if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + src_maxburst = src_maxburst ? src_maxburst : 8; break; case DMA_DEV_TO_MEM: - src_burst = convert_burst(sconfig->src_maxburst); - src_width = convert_buswidth(sconfig->src_addr_width); - dst_burst = convert_burst(sconfig->dst_maxburst ? - sconfig->dst_maxburst : 8); - dst_width = convert_buswidth(sconfig->dst_addr_width != - DMA_SLAVE_BUSWIDTH_UNDEFINED ? - sconfig->dst_addr_width : - DMA_SLAVE_BUSWIDTH_4_BYTES); + if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dst_maxburst = dst_maxburst ? dst_maxburst : 8; break; default: return -EINVAL; } - if (src_burst < 0) - return src_burst; - if (src_width < 0) - return src_width; - if (dst_burst < 0) - return dst_burst; - if (dst_width < 0) - return dst_width; + if (!(BIT(src_addr_width) & sdev->slave.src_addr_widths)) + return -EINVAL; + if (!(BIT(dst_addr_width) & sdev->slave.dst_addr_widths)) + return -EINVAL; + if (!(BIT(src_maxburst) & sdev->cfg->src_burst_lengths)) + return -EINVAL; + if (!(BIT(dst_maxburst) & sdev->cfg->dst_burst_lengths)) + return -EINVAL; + + src_width = convert_buswidth(src_addr_width); + dst_width = convert_buswidth(dst_addr_width); + dst_burst = convert_burst(dst_maxburst); + src_burst = convert_burst(src_maxburst); *p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) | DMA_CHAN_CFG_DST_WIDTH(dst_width); @@ -1041,6 +1041,8 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .nr_max_requests = 30, .nr_max_vchans = 53, .set_burst_length = sun6i_set_burst_length_a31, + .src_burst_lengths = BIT(1) | BIT(8), + .dst_burst_lengths = BIT(1) | BIT(8), }; /
[PATCH v4 05/11] dmaengine: sun6i: Move number of pchans/vchans/request to device struct
Preparatory patch: If the same compatible is used for different SoCs which have a common register layout, but different number of channels, the channel count can no longer be stored in the config. Store it in the device structure instead. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v4: None Changes in v3: None Changes in v2: None drivers/dma/sun6i-dma.c | 26 -- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index eb45ab39eddd..7fce976a13d8 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -188,6 +188,9 @@ struct sun6i_dma_dev { struct sun6i_pchan *pchans; struct sun6i_vchan *vchans; const struct sun6i_dma_config *cfg; + u32 num_pchans; + u32 num_vchans; + u32 max_request; }; static struct device *chan2dev(struct dma_chan *chan) @@ -434,7 +437,6 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan) static void sun6i_dma_tasklet(unsigned long data) { struct sun6i_dma_dev *sdev = (struct sun6i_dma_dev *)data; - const struct sun6i_dma_config *cfg = sdev->cfg; struct sun6i_vchan *vchan; struct sun6i_pchan *pchan; unsigned int pchan_alloc = 0; @@ -462,7 +464,7 @@ static void sun6i_dma_tasklet(unsigned long data) } spin_lock_irq(&sdev->lock); - for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { + for (pchan_idx = 0; pchan_idx < sdev->num_pchans; pchan_idx++) { pchan = &sdev->pchans[pchan_idx]; if (pchan->vchan || list_empty(&sdev->pending)) @@ -483,7 +485,7 @@ static void sun6i_dma_tasklet(unsigned long data) } spin_unlock_irq(&sdev->lock); - for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { + for (pchan_idx = 0; pchan_idx < sdev->num_pchans; pchan_idx++) { if (!(pchan_alloc & BIT(pchan_idx))) continue; @@ -505,7 +507,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id) int i, j, ret = IRQ_NONE; u32 status; - for (i = 0; i < sdev->cfg->nr_max_channels / DMA_IRQ_CHAN_NR; i++) { + for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) { status = readl(sdev->base + DMA_IRQ_STAT(i)); if (!status) continue; @@ -985,7 +987,7 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec, struct dma_chan *chan; u8 port = dma_spec->args[0]; - if (port > sdev->cfg->nr_max_requests) + if (port > sdev->max_request) return NULL; chan = dma_get_any_slave_channel(&sdev->slave); @@ -1018,7 +1020,7 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev) { int i; - for (i = 0; i < sdev->cfg->nr_max_vchans; i++) { + for (i = 0; i < sdev->num_vchans; i++) { struct sun6i_vchan *vchan = &sdev->vchans[i]; list_del(&vchan->vc.chan.device_node); @@ -1224,26 +1226,30 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; sdc->slave.dev = &pdev->dev; - sdc->pchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_channels, + sdc->num_pchans = sdc->cfg->nr_max_channels; + sdc->num_vchans = sdc->cfg->nr_max_vchans; + sdc->max_request = sdc->cfg->nr_max_requests; + + sdc->pchans = devm_kcalloc(&pdev->dev, sdc->num_pchans, sizeof(struct sun6i_pchan), GFP_KERNEL); if (!sdc->pchans) return -ENOMEM; - sdc->vchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_vchans, + sdc->vchans = devm_kcalloc(&pdev->dev, sdc->num_vchans, sizeof(struct sun6i_vchan), GFP_KERNEL); if (!sdc->vchans) return -ENOMEM; tasklet_init(&sdc->task, sun6i_dma_tasklet, (unsigned long)sdc); - for (i = 0; i < sdc->cfg->nr_max_channels; i++) { + for (i = 0; i < sdc->num_pchans; i++) { struct sun6i_pchan *pchan = &sdc->pchans[i]; pchan->idx = i; pchan->base = sdc->base + 0x100 + i * 0x40; } - for (i = 0; i < sdc->cfg->nr_max_vchans; i++) { + for (i = 0; i < sdc->num_vchans; i++) { struct sun6i_vchan *vchan = &sdc->vchans[i]; INIT_LIST_HEAD(&vchan->node); -- 2.14.1
[PATCH v4 00/11] dmaengine: sun6i: Fixes for H3/A83T, enable A64
Commit 3a03ea763a67 ("dmaengine: sun6i: Add support for Allwinner A83T (sun8i) variant") and commit f008db8c00c1 ("dmaengine: sun6i: Add support for Allwinner H3 (sun8i) variant") added support for the A83T resp. H3, but missed some differences between the original A31 and A83T/H3. The first patch adds a callback to the controller config to set the clock autogating register of different SoC generations, i.e. A31, A23+A83T, H3+later, and uses it to for the correct clock autogating setting. The second patch adds a callback for the burst length setting in the channel config register, which has different field offsets and new burst widths/lengths, which differs between H3 and earlier generations The third patch restructures some code required for the fourth patch and adds the burst lengths to the controller config. The fourth patch adds the burst widths to the config and adds the handling of the H3 specific burst widths. Patch 5 restructures the code to decouple some controller details (e.g. channel count) from the compatible string/the config. Patches 6, 7 and 8 introduce and use the "dma-chans" property for the A64. Although register compatible to the H3, the channel count differs and thus it requires a new compatible. To avoid introduction of new compatibles for each minor variation, anything but the register model is moved to devicetree properties. There is at least one SoC (R40) which can then reuse the A64 compatible, the same would have worked for A83T+V3s. Patches 9 and 10 add the DMA controller node to the devicetree and add the DMA controller reference to the SPI nodes. Patch 11 fixes a small error in the devicetree binding example. Changes in v4: - Correct callback function signature, pass pointer to controller - sun6i_dma_dev refers to sun6i_dma_config and vice versa, add forward declaration - Pass reference to config instead of config itself - Fix config initialization - remove range checks for dma-channels/dma-requests DT properties - Split minor fix in devicetree example from patch 6/10 Changes in v3: - Check for callback instead of using a no-op callback - Drop leading 0 from unit name in DT example - Omit default values from sun50i_a64_dma_cfg definition - Drop leading 0 from dma controller unit name Changes in v2: - Use callback for autogating instead of variable for different SoC generations - Use controller specific callback for burst length setting - Store burst lengths in config instead of device structure - Store burst widths in config - Set default number of dma-request if not provided in config or devicetree Stefan Brüns (11): dmaengine: sun6i: Correct setting of clock autogating register for A83T/H3 dmaengine: sun6i: Correct burst length field offsets for H3 dmaengine: sun6i: Restructure code to allow extension for new SoCs dmaengine: sun6i: Enable additional burst lengths/widths on H3 dmaengine: sun6i: Move number of pchans/vchans/request to device struct arm64: allwinner: a64: Add devicetree binding for DMA controller dmaengine: sun6i: Retrieve channel count/max request from devicetree dmaengine: sun6i: Add support for Allwinner A64 and compatibles arm64: allwinner: a64: Add device node for DMA controller arm64: allwinner: a64: add dma controller references to spi nodes arm: allwinner: Correct unit name in devicetree binding example .../devicetree/bindings/dma/sun6i-dma.txt | 28 ++- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 15 ++ drivers/dma/sun6i-dma.c| 251 - 3 files changed, 235 insertions(+), 59 deletions(-) -- 2.14.1
[PATCH v4 01/11] dmaengine: sun6i: Correct setting of clock autogating register for A83T/H3
The H83T uses a compatible string different from the A23, but requires the same clock autogating register setting. The H3 also requires setting the clock autogating register, but has the register at a different offset. Add three suitable callbacks for the existing controller generations and set it in the controller config structure. Signed-off-by: Stefan Brüns Acked-by: Maxime Ripard --- Changes in v4: - Correct callback function signature, pass pointer to controller - sun6i_dma_dev refers to sun6i_dma_config and vice versa, add forward declaration - Fix config initialization Changes in v3: - Check for callback instead of using a no-op callback Changes in v2: - Use callback for autogating instead of variable for different SoC generations drivers/dma/sun6i-dma.c | 28 +++- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index bcd496edc70f..6f9d7c4ac7c0 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -48,6 +48,9 @@ #define SUN8I_DMA_GATE 0x20 #define SUN8I_DMA_GATE_ENABLE 0x4 +#define SUNXI_H3_SECURE_REG0x20 +#define SUNXI_H3_DMA_GATE 0x28 +#define SUNXI_H3_DMA_GATE_ENABLE 0x4 /* * Channels specific registers */ @@ -90,6 +93,9 @@ #define NORMAL_WAIT8 #define DRQ_SDRAM 1 +/* forward declaration */ +struct sun6i_dma_dev; + /* * Hardware channels / ports representation * @@ -111,7 +117,7 @@ struct sun6i_dma_config { * however these SoCs really have and need this bit, as seen in the * BSP kernel source code. */ - bool gate_needed; + void (*clock_autogate_enable)(struct sun6i_dma_dev *); }; /* @@ -267,6 +273,16 @@ static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) return addr_width >> 1; } +static void sun6i_enable_clock_autogate_a23(struct sun6i_dma_dev *sdev) +{ + writel(SUN8I_DMA_GATE_ENABLE, sdev->base + SUN8I_DMA_GATE); +} + +static void sun6i_enable_clock_autogate_h3(struct sun6i_dma_dev *sdev) +{ + writel(SUNXI_H3_DMA_GATE_ENABLE, sdev->base + SUNXI_H3_DMA_GATE); +} + static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) { struct sun6i_desc *txd = pchan->desc; @@ -1020,13 +1036,14 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 24, .nr_max_vchans = 37, - .gate_needed = true, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23, }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 28, .nr_max_vchans = 39, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23, }; /* @@ -1038,6 +1055,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_channels = 12, .nr_max_requests = 27, .nr_max_vchans = 34, + .clock_autogate_enable = sun6i_enable_clock_autogate_h3, }; /* @@ -1049,7 +1067,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 23, .nr_max_vchans = 24, - .gate_needed = true, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23, }; static const struct of_device_id sun6i_dma_match[] = { @@ -1199,8 +1217,8 @@ static int sun6i_dma_probe(struct platform_device *pdev) goto err_dma_unregister; } - if (sdc->cfg->gate_needed) - writel(SUN8I_DMA_GATE_ENABLE, sdc->base + SUN8I_DMA_GATE); + if (sdc->cfg->clock_autogate_enable) + sdc->cfg->clock_autogate_enable(sdc); return 0; -- 2.14.1
[PATCH 2/3] iio: adc: ina2xx: Adhere to documented ABI, use Ohm instead of uOhm
According to the ABI documentation, the shunt resistor value should be specificied in Ohm. As this is also used/documented for the MAX9611, use the same for the INA2xx driver. This poses an ABI break for anyone actually altering the shunt value through the sysfs interface, it does not alter the default value nor a value set from the devicetree. Minor change: Fix comment, 1mA is 10^-3A. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 18 +- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 361fb4e459d5..1930f853e8c5 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -127,7 +127,7 @@ struct ina2xx_chip_info { struct task_struct *task; const struct ina2xx_config *config; struct mutex state_lock; - unsigned int shunt_resistor; + unsigned int shunt_resistor_uohm; int avg; int int_time_vbus; /* Bus voltage integration time uS */ int int_time_vshunt; /* Shunt voltage integration time uS */ @@ -444,7 +444,7 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev, /* * Set current LSB to 1mA, shunt is in uOhms * (equation 13 in datasheet). We hardcode a Current_LSB - * of 1.0 x10-6. The only remaining parameter is RShunt. + * of 1.0 x10-3. The only remaining parameter is RShunt. * There is no need to expose the CALIBRATION register * to the user for now. But we need to reset this register * if the user updates RShunt after driver init, e.g upon @@ -453,7 +453,7 @@ static ssize_t ina2xx_allow_async_readout_store(struct device *dev, static int ina2xx_set_calibration(struct ina2xx_chip_info *chip) { u16 regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor, - chip->shunt_resistor); + chip->shunt_resistor_uohm); return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval); } @@ -463,7 +463,7 @@ static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int val) if (val <= 0 || val > chip->config->calibration_factor) return -EINVAL; - chip->shunt_resistor = val; + chip->shunt_resistor_uohm = val; return 0; } @@ -473,8 +473,9 @@ static ssize_t ina2xx_shunt_resistor_show(struct device *dev, char *buf) { struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); + int vals[2] = { chip->shunt_resistor_uohm, 100 }; - return sprintf(buf, "%d\n", chip->shunt_resistor); + return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals); } static ssize_t ina2xx_shunt_resistor_store(struct device *dev, @@ -482,14 +483,13 @@ static ssize_t ina2xx_shunt_resistor_store(struct device *dev, const char *buf, size_t len) { struct ina2xx_chip_info *chip = iio_priv(dev_to_iio_dev(dev)); - unsigned long val; - int ret; + int val, val_fract, ret; - ret = kstrtoul((const char *) buf, 10, &val); + ret = iio_str_to_fixpoint(buf, 10, &val, &val_fract); if (ret) return ret; - ret = set_shunt_resistor(chip, val); + ret = set_shunt_resistor(chip, val * 100 + val_fract); if (ret) return ret; -- 2.14.1
[PATCH 0/3] iio: adc: ina2xx: Fixes and extensions
The first patch fixes an issue with the conversion-ready status flag showing up in the bus voltage raw value. The second patch changes the the shunt resistor value attribute to ohms (instead of microohms), to match both ABI documentation and other shunt resistor users, i.e. the MAX9611 IIO driver. The third patch adds support for the INA219/220 specific bus voltage range and shunt voltage PGA. Stefan Brüns (3): iio: adc: ina2xx: Mask flag bits in bus voltage register iio: adc: ina2xx: Adhere to documented ABI, use Ohm instead of uOhm iio: adc: ina2xx: Allow setting Shunt Voltage PGA gain and Bus Voltage range drivers/iio/adc/ina2xx-adc.c | 143 +++ 1 file changed, 130 insertions(+), 13 deletions(-) -- 2.14.1
[PATCH 3/3] iio: adc: ina2xx: Allow setting Shunt Voltage PGA gain and Bus Voltage range
Reducing shunt and bus voltage range improves the accuracy, so allow altering the default settings. Both settings are exposed as gain values. While for the shunt voltage this is straightforward, the bus range settings of 32V (default) and 16V are mapped to gain values of 1 resp. 2, to provide a uniform API to userspace. As the gain settings are incorporated into the raw values by the sensor itself, adjusting of the scale attributes is not necessary. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 111 +-- 1 file changed, 108 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 1930f853e8c5..7e9ce43ad15d 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -48,8 +48,10 @@ #define INA2XX_MAX_REGISTERS8 /* settings - depend on use case */ -#define INA219_CONFIG_DEFAULT 0x399F /* PGA=8 */ +#define INA219_CONFIG_DEFAULT 0x399F /* PGA=1/8, BRNG=32V */ #define INA219_DEFAULT_IT 532 +#define INA219_DEFAULT_BRNG 1 /* 32V */ +#define INA219_DEFAULT_PGA 125 /* 1000/8 */ #define INA226_CONFIG_DEFAULT 0x4327 #define INA226_DEFAULT_AVG 4 #define INA226_DEFAULT_IT 1110 @@ -62,6 +64,14 @@ */ #define INA2XX_MODE_MASK GENMASK(3, 0) +/* Gain for VShunt: 1/8 (default), 1/4, 1/2, 1 */ +#define INA219_PGA_MASKGENMASK(12, 11) +#define INA219_SHIFT_PGA(val) ((val) << 11) + +/* VBus range: 32V (default), 16V */ +#define INA219_BRNG_MASK BIT(13) +#define INA219_SHIFT_BRNG(val) ((val) << 13) + /* Averaging for VBus/VShunt/Power */ #define INA226_AVG_MASKGENMASK(11, 9) #define INA226_SHIFT_AVG(val) ((val) << 9) @@ -131,6 +141,8 @@ struct ina2xx_chip_info { int avg; int int_time_vbus; /* Bus voltage integration time uS */ int int_time_vshunt; /* Shunt voltage integration time uS */ + int range_vbus; /* Bus voltage maximum in V */ + int pga_gain_vshunt; /* Shunt voltage PGA gain */ bool allow_async_readout; }; @@ -227,6 +239,18 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, *val = 1; return IIO_VAL_INT; } + + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *val = chip->pga_gain_vshunt; + *val2 = 1000; + return IIO_VAL_FRACTIONAL; + + case INA2XX_BUS_VOLTAGE: + *val = chip->range_vbus == 32 ? 1 : 2; + return IIO_VAL_INT; + } } return -EINVAL; @@ -361,6 +385,74 @@ static int ina219_set_int_time_vshunt(struct ina2xx_chip_info *chip, return 0; } +static const int ina219_vbus_range_tab[] = { 1, 2 }; +static int ina219_set_vbus_range_denom(struct ina2xx_chip_info *chip, + unsigned int range, + unsigned int *config) +{ + if (range == 1) + chip->range_vbus = 32; + else if (range == 2) + chip->range_vbus = 16; + else + return -EINVAL; + + *config &= ~INA219_BRNG_MASK; + *config |= INA219_SHIFT_BRNG(range == 1 ? 1 : 0) & INA219_BRNG_MASK; + + return 0; +} + +static const int ina219_vshunt_gain_tab[] = { 125, 250, 500, 1000 }; +static const int ina219_vshunt_gain_frac[] = { + 125, 1000, 250, 1000, 500, 1000, 1000, 1000 }; + +static int ina219_set_vshunt_pga_gain(struct ina2xx_chip_info *chip, + unsigned int gain, + unsigned int *config) +{ + int bits; + + if (gain < 125 || gain > 1000) + return -EINVAL; + + bits = find_closest(gain, ina219_vshunt_gain_tab, + ARRAY_SIZE(ina219_vshunt_gain_tab)); + + chip->pga_gain_vshunt = ina219_vshunt_gain_tab[bits]; + bits = 3 - bits; + + *config &= ~INA219_PGA_MASK; + *config |= INA219_SHIFT_PGA(bits) & INA219_PGA_MASK; + + return 0; +} + +static int ina2xx_read_avail(struct iio_dev *indio_dev, +struct iio_chan_spec const *chan, +const int **vals, int *type, int *length, +long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_HARDWAREGAIN: + switch (chan->address) { + case INA2XX_SHUNT_VOLTAGE: + *type = IIO_VAL_FRACTIONAL; + *length = sizeof(ina219_vshunt_gain_frac) / sizeof(int); + *vals = ina219_vshunt_gain_frac; + return IIO_AVAIL_LIST; + +
[PATCH 1/3] iio: adc: ina2xx: Mask flag bits in bus voltage register
Lower bits of the INA219/220 bus voltage register are conversion status flags, properly mask the value. Signed-off-by: Stefan Brüns --- drivers/iio/adc/ina2xx-adc.c | 14 +- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index f387b972e4f4..361fb4e459d5 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -44,7 +44,6 @@ #define INA226_MASK_ENABLE 0x06 #define INA226_CVRFBIT(3) -#define INA219_CNVRBIT(1) #define INA2XX_MAX_REGISTERS8 @@ -79,6 +78,11 @@ #define INA226_ITS_MASKGENMASK(5, 3) #define INA226_SHIFT_ITS(val) ((val) << 3) +/* INA219 Bus voltage register, low bits are flags */ +#define INA219_OVF BIT(0) +#define INA219_CNVRBIT(1) +#define INA219_BUS_VOLTAGE_MASKGENMASK(16, 3) + /* Cosmetic macro giving the sampling period for a full P=UxI cycle */ #define SAMPLING_PERIOD(c) ((c->int_time_vbus + c->int_time_vshunt) \ * c->avg) @@ -170,6 +174,10 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev, else *val = regval; + if ((chip->config->chip_id == ina219) && + (chan->address == INA2XX_SHUNT_VOLTAGE)) + *val &= INA219_BUS_VOLTAGE_MASK; + return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: @@ -639,6 +647,10 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev) if (ret < 0) return ret; + if ((chip->config->chip_id == ina219) && + (INA2XX_SHUNT_VOLTAGE + bit == INA2XX_BUS_VOLTAGE)) + val &= INA219_BUS_VOLTAGE_MASK; + data[i++] = val; if (INA2XX_SHUNT_VOLTAGE + bit == INA2XX_POWER) -- 2.14.1
[PATCH v2 06/10] arm64: allwinner: a64: Add devicetree binding for DMA controller
The A64 is register compatible with the H3, but has a different number of dma channels and request ports. Attach additional properties to the node to allow future reuse of the compatible for controllers with different number of channels/requests. If dma-requests is not specified, the register layout defined maximum of 32 is used. Signed-off-by: Stefan Brüns --- .../devicetree/bindings/dma/sun6i-dma.txt | 26 ++ 1 file changed, 26 insertions(+) diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt index 98fbe1a5c6dd..6ebc79f95202 100644 --- a/Documentation/devicetree/bindings/dma/sun6i-dma.txt +++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt @@ -27,6 +27,32 @@ Example: #dma-cells = <1>; }; +-- +For A64 DMA controller: + +Required properties: +- compatible: "allwinner,sun50i-a64-dma" +- dma-channels: Number of DMA channels supported by the controller. + Refer to Documentation/devicetree/bindings/dma/dma.txt +- all properties above, i.e. reg, interrupts, clocks, resets and #dma-cells + +Optional properties: +- dma-requests: Number of DMA request signals supported by the controller. + Refer to Documentation/devicetree/bindings/dma/dma.txt + +Example: + dma: dma-controller@01c02000 { + compatible = "allwinner,sun50i-a64-dma"; + reg = <0x01c02000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_DMA>; + dma-channels = <8>; + dma-requests = <27>; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + }; +-- + Clients: DMA clients connected to the A31 DMA controller must use the format -- 2.14.1
[PATCH v2 08/10] dmaengine: sun6i: Add support for Allwinner A64 and compatibles
The A64 SoC has the same dma engine as the H3 (sun8i), with a reduced amount of physical channels. To allow future reuse of the compatible, leave the channel count etc. in the config data blank and retrieve it from the devicetree. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 23 +++ 1 file changed, 23 insertions(+) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index b5ecc97a0d5a..118b29bb1eac 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -1127,6 +1127,28 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); }; +/* + * The A64 binding uses the number of dma channels from the + * device tree node. + */ +static struct sun6i_dma_config sun50i_a64_dma_cfg = { + .nr_max_channels = 0, + .nr_max_requests = 0, + .nr_max_vchans = 0, + .clock_autogate_enable = sun6i_enable_clock_autogate_h3; + .set_burst_length = sun6i_set_burst_length_h3; + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); +}; + /* * The V3s have only 8 physical channels, a maximum DRQ port id of 23, * and a total of 24 usable source and destination endpoints. @@ -1154,6 +1176,7 @@ static const struct of_device_id sun6i_dma_match[] = { { .compatible = "allwinner,sun8i-a83t-dma", .data = &sun8i_a83t_dma_cfg }, { .compatible = "allwinner,sun8i-h3-dma", .data = &sun8i_h3_dma_cfg }, { .compatible = "allwinner,sun8i-v3s-dma", .data = &sun8i_v3s_dma_cfg }, + { .compatible = "allwinner,sun50i-a64-dma", .data = &sun50i_a64_dma_cfg }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, sun6i_dma_match); -- 2.14.1
[PATCH v2 07/10] dmaengine: sun6i: Retrieve channel count/max request from devicetree
To avoid introduction of a new compatible for each small SoC/DMA controller variation, move the definition of the channel count to the devicetree. The number of vchans is no longer explicit, but limited by the highest port/DMA request number. The result is a slight overallocation for SoCs with a sparse port mapping. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 37 - 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 245a147f718f..b5ecc97a0d5a 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -42,6 +42,9 @@ #define DMA_STAT 0x30 +/* Offset between DMA_IRQ_EN and DMA_IRQ_STAT limits number of channels */ +#define DMA_MAX_CHANNELS (DMA_IRQ_CHAN_NR * 0x10 / 4) + /* * sun8i specific registers */ @@ -65,7 +68,8 @@ #define DMA_CHAN_LLI_ADDR 0x08 #define DMA_CHAN_CUR_CFG 0x0c -#define DMA_CHAN_CFG_SRC_DRQ(x)((x) & 0x1f) +#define DMA_CHAN_MAX_DRQ 0x1f +#define DMA_CHAN_CFG_SRC_DRQ(x)((x) & DMA_CHAN_MAX_DRQ) #define DMA_CHAN_CFG_SRC_IO_MODE BIT(5) #define DMA_CHAN_CFG_SRC_LINEAR_MODE (0 << 5) #define DMA_CHAN_CFG_SRC_BURST_A31(x) (((x) & 0x3) << 7) @@ -1157,6 +1161,7 @@ MODULE_DEVICE_TABLE(of, sun6i_dma_match); static int sun6i_dma_probe(struct platform_device *pdev) { const struct of_device_id *device; + struct device_node *np = pdev->dev.of_node; struct sun6i_dma_dev *sdc; struct resource *res; int ret, i; @@ -1232,6 +1237,36 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->num_vchans = sdc->cfg->nr_max_vchans; sdc->max_request = sdc->cfg->nr_max_requests; + ret = of_property_read_u32(np, "dma-channels", &sdc->num_pchans); + if (ret && !sdc->num_pchans) { + dev_err(&pdev->dev, "Can't get dma-channels.\n"); + return ret; + } + + if (sdc->num_pchans > DMA_MAX_CHANNELS) { + dev_err(&pdev->dev, "Number of dma-channels out of range.\n"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "dma-requests", &sdc->max_request); + if (ret && !sdc->max_request) { + dev_info(&pdev->dev, "Missing dma-requests, using %u.\n", +DMA_CHAN_MAX_DRQ); + sdc->max_request = DMA_CHAN_MAX_DRQ; + } + + if (sdc->max_request > DMA_CHAN_MAX_DRQ) { + dev_err(&pdev->dev, "Value of dma-requests out of range.\n"); + return -EINVAL; + } + + /* +* If the number of vchans is not specified, derive it from the +* highest port number, at most one channel per port and direction. +*/ + if (!sdc->num_vchans) + sdc->num_vchans = 2 * (sdc->max_request + 1); + sdc->pchans = devm_kcalloc(&pdev->dev, sdc->num_pchans, sizeof(struct sun6i_pchan), GFP_KERNEL); if (!sdc->pchans) -- 2.14.1
[PATCH v2 10/10] arm64: allwinner: a64: add dma controller references to spi nodes
The spi controller nodes omit the dma controller/channel references, add it. This does not yet enable DMA for SPI transfers, as the spi-sun6i driver lacks support for DMA, but always uses PIO to the FIFO. Signed-off-by: Stefan Brüns --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 4 1 file changed, 4 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index 4f060ecdb061..ec71c48b393d 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -476,6 +476,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI0>, <&ccu CLK_SPI0>; clock-names = "ahb", "mod"; + dmas = <&dma 23>, <&dma 23>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi0_pins>; resets = <&ccu RST_BUS_SPI0>; @@ -491,6 +493,8 @@ interrupts = ; clocks = <&ccu CLK_BUS_SPI1>, <&ccu CLK_SPI1>; clock-names = "ahb", "mod"; + dmas = <&dma 24>, <&dma 24>; + dma-names = "rx", "tx"; pinctrl-names = "default"; pinctrl-0 = <&spi1_pins>; resets = <&ccu RST_BUS_SPI1>; -- 2.14.1
[PATCH v2 05/10] dmaengine: sun6i: Move number of pchans/vchans/request to device struct
Preparatory patch: If the same compatible is used for different SoCs which have a common register layout, but different number of channels, the channel count can no longer be stored in the config. Store it in the device structure instead. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 26 -- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index f2ee914cd755..245a147f718f 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -185,6 +185,9 @@ struct sun6i_dma_dev { struct sun6i_pchan *pchans; struct sun6i_vchan *vchans; const struct sun6i_dma_config *cfg; + u32 num_pchans; + u32 num_vchans; + u32 max_request; }; static struct device *chan2dev(struct dma_chan *chan) @@ -435,7 +438,6 @@ static int sun6i_dma_start_desc(struct sun6i_vchan *vchan) static void sun6i_dma_tasklet(unsigned long data) { struct sun6i_dma_dev *sdev = (struct sun6i_dma_dev *)data; - const struct sun6i_dma_config *cfg = sdev->cfg; struct sun6i_vchan *vchan; struct sun6i_pchan *pchan; unsigned int pchan_alloc = 0; @@ -463,7 +465,7 @@ static void sun6i_dma_tasklet(unsigned long data) } spin_lock_irq(&sdev->lock); - for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { + for (pchan_idx = 0; pchan_idx < sdev->num_pchans; pchan_idx++) { pchan = &sdev->pchans[pchan_idx]; if (pchan->vchan || list_empty(&sdev->pending)) @@ -484,7 +486,7 @@ static void sun6i_dma_tasklet(unsigned long data) } spin_unlock_irq(&sdev->lock); - for (pchan_idx = 0; pchan_idx < cfg->nr_max_channels; pchan_idx++) { + for (pchan_idx = 0; pchan_idx < sdev->num_pchans; pchan_idx++) { if (!(pchan_alloc & BIT(pchan_idx))) continue; @@ -506,7 +508,7 @@ static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id) int i, j, ret = IRQ_NONE; u32 status; - for (i = 0; i < sdev->cfg->nr_max_channels / DMA_IRQ_CHAN_NR; i++) { + for (i = 0; i < sdev->num_pchans / DMA_IRQ_CHAN_NR; i++) { status = readl(sdev->base + DMA_IRQ_STAT(i)); if (!status) continue; @@ -986,7 +988,7 @@ static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec, struct dma_chan *chan; u8 port = dma_spec->args[0]; - if (port > sdev->cfg->nr_max_requests) + if (port > sdev->max_request) return NULL; chan = dma_get_any_slave_channel(&sdev->slave); @@ -1019,7 +1021,7 @@ static inline void sun6i_dma_free(struct sun6i_dma_dev *sdev) { int i; - for (i = 0; i < sdev->cfg->nr_max_vchans; i++) { + for (i = 0; i < sdev->num_vchans; i++) { struct sun6i_vchan *vchan = &sdev->vchans[i]; list_del(&vchan->vc.chan.device_node); @@ -1226,26 +1228,30 @@ static int sun6i_dma_probe(struct platform_device *pdev) sdc->slave.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; sdc->slave.dev = &pdev->dev; - sdc->pchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_channels, + sdc->num_pchans = sdc->cfg->nr_max_channels; + sdc->num_vchans = sdc->cfg->nr_max_vchans; + sdc->max_request = sdc->cfg->nr_max_requests; + + sdc->pchans = devm_kcalloc(&pdev->dev, sdc->num_pchans, sizeof(struct sun6i_pchan), GFP_KERNEL); if (!sdc->pchans) return -ENOMEM; - sdc->vchans = devm_kcalloc(&pdev->dev, sdc->cfg->nr_max_vchans, + sdc->vchans = devm_kcalloc(&pdev->dev, sdc->num_vchans, sizeof(struct sun6i_vchan), GFP_KERNEL); if (!sdc->vchans) return -ENOMEM; tasklet_init(&sdc->task, sun6i_dma_tasklet, (unsigned long)sdc); - for (i = 0; i < sdc->cfg->nr_max_channels; i++) { + for (i = 0; i < sdc->num_pchans; i++) { struct sun6i_pchan *pchan = &sdc->pchans[i]; pchan->idx = i; pchan->base = sdc->base + 0x100 + i * 0x40; } - for (i = 0; i < sdc->cfg->nr_max_vchans; i++) { + for (i = 0; i < sdc->num_vchans; i++) { struct sun6i_vchan *vchan = &sdc->vchans[i]; INIT_LIST_HEAD(&vchan->node); -- 2.14.1
[PATCH v2 01/10] dmaengine: sun6i: Correct setting of clock autogating register for A83T/H3
The H83T uses a compatible string different from the A23, but requires the same clock autogating register setting. The H3 also requires setting the clock autogating register, but has the register at a different offset. Add three suitable callbacks for the existing controller generations and set it in the controller config structure. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 31 ++- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index bcd496edc70f..45bcd5271d94 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -48,6 +48,9 @@ #define SUN8I_DMA_GATE 0x20 #define SUN8I_DMA_GATE_ENABLE 0x4 +#define SUNXI_H3_SECURE_REG0x20 +#define SUNXI_H3_DMA_GATE 0x28 +#define SUNXI_H3_DMA_GATE_ENABLE 0x4 /* * Channels specific registers */ @@ -111,7 +114,7 @@ struct sun6i_dma_config { * however these SoCs really have and need this bit, as seen in the * BSP kernel source code. */ - bool gate_needed; + void (*clock_autogate_enable)(); }; /* @@ -267,6 +270,20 @@ static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) return addr_width >> 1; } +static void sun6i_enable_clock_autogate_noop(struct sun6i_dma_dev *sdev) +{ +} + +static void sun6i_enable_clock_autogate_a23(struct sun6i_dma_dev *sdev) +{ + writel(SUN8I_DMA_GATE_ENABLE, sdev->base + SUN8I_DMA_GATE); +} + +static void sun6i_enable_clock_autogate_h3(struct sun6i_dma_dev *sdev) +{ + writel(SUNXI_H3_DMA_GATE_ENABLE, sdev->base + SUNXI_H3_DMA_GATE); +} + static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) { struct sun6i_desc *txd = pchan->desc; @@ -1009,6 +1026,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .nr_max_channels = 16, .nr_max_requests = 30, .nr_max_vchans = 53, + .clock_autogate_enable = sun6i_enable_clock_autogate_noop; }; /* @@ -1020,24 +1038,28 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 24, .nr_max_vchans = 37, - .gate_needed = true, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23; }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 28, .nr_max_vchans = 39, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23; }; /* * The H3 has 12 physical channels, a maximum DRQ port id of 27, * and a total of 34 usable source and destination endpoints. + * It also supports additional burst lengths and bus widths, + * and the burst length fields have different offsets. */ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_channels = 12, .nr_max_requests = 27, .nr_max_vchans = 34, + .clock_autogate_enable = sun6i_enable_clock_autogate_h3; }; /* @@ -1049,7 +1071,7 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = { .nr_max_channels = 8, .nr_max_requests = 23, .nr_max_vchans = 24, - .gate_needed = true, + .clock_autogate_enable = sun6i_enable_clock_autogate_a23; }; static const struct of_device_id sun6i_dma_match[] = { @@ -1199,8 +1221,7 @@ static int sun6i_dma_probe(struct platform_device *pdev) goto err_dma_unregister; } - if (sdc->cfg->gate_needed) - writel(SUN8I_DMA_GATE_ENABLE, sdc->base + SUN8I_DMA_GATE); + sdc->cfg->clock_autogate_enable(); return 0; -- 2.14.1
[PATCH v2 09/10] arm64: allwinner: a64: Add device node for DMA controller
The A64 SoC has a DMA controller that supports 8 DMA channels to and from various peripherals. The last used DRQ port is 27. Add a device node for it. Signed-off-by: Stefan Brüns --- arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 11 +++ 1 file changed, 11 insertions(+) diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi index e9a9d7fb01c8..4f060ecdb061 100644 --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi @@ -136,6 +136,17 @@ reg = <0x01c0 0x1000>; }; + dma: dma-controller@01c02000 { + compatible = "allwinner,sun50i-a64-dma"; + reg = <0x01c02000 0x1000>; + interrupts = ; + clocks = <&ccu CLK_BUS_DMA>; + dma-channels = <8>; + dma-requests = <27>; + resets = <&ccu RST_BUS_DMA>; + #dma-cells = <1>; + }; + mmc0: mmc@1c0f000 { compatible = "allwinner,sun50i-a64-mmc"; reg = <0x01c0f000 0x1000>; -- 2.14.1
[PATCH v2 02/10] dmaengine: sun6i: Correct burst length field offsets for H3
For the H3, the burst lengths field offsets in the channel configuration register differs from earlier SoC generations. Using the A31 register macros actually configured the H3 controller do to bursts of length 1 always, which although working leads to higher bus utilisation. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 36 +++- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 45bcd5271d94..a6fc066a0ac6 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -68,13 +68,15 @@ #define DMA_CHAN_CFG_SRC_DRQ(x)((x) & 0x1f) #define DMA_CHAN_CFG_SRC_IO_MODE BIT(5) #define DMA_CHAN_CFG_SRC_LINEAR_MODE (0 << 5) -#define DMA_CHAN_CFG_SRC_BURST(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_A31(x) (((x) & 0x3) << 7) +#define DMA_CHAN_CFG_SRC_BURST_H3(x) (((x) & 0x3) << 6) #define DMA_CHAN_CFG_SRC_WIDTH(x) (((x) & 0x3) << 9) #define DMA_CHAN_CFG_DST_DRQ(x)(DMA_CHAN_CFG_SRC_DRQ(x) << 16) #define DMA_CHAN_CFG_DST_IO_MODE (DMA_CHAN_CFG_SRC_IO_MODE << 16) #define DMA_CHAN_CFG_DST_LINEAR_MODE (DMA_CHAN_CFG_SRC_LINEAR_MODE << 16) -#define DMA_CHAN_CFG_DST_BURST(x) (DMA_CHAN_CFG_SRC_BURST(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_A31(x) (DMA_CHAN_CFG_SRC_BURST_A31(x) << 16) +#define DMA_CHAN_CFG_DST_BURST_H3(x) (DMA_CHAN_CFG_SRC_BURST_H3(x) << 16) #define DMA_CHAN_CFG_DST_WIDTH(x) (DMA_CHAN_CFG_SRC_WIDTH(x) << 16) #define DMA_CHAN_CUR_SRC 0x10 @@ -115,6 +117,7 @@ struct sun6i_dma_config { * BSP kernel source code. */ void (*clock_autogate_enable)(); + void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); }; /* @@ -284,6 +287,18 @@ static void sun6i_enable_clock_autogate_h3(struct sun6i_dma_dev *sdev) writel(SUNXI_H3_DMA_GATE_ENABLE, sdev->base + SUNXI_H3_DMA_GATE); } +static void sun6i_set_burst_length_a31(u32 *p_cfg, s8 src_burst, s8 dst_burst) +{ + *p_cfg |= DMA_CHAN_CFG_SRC_BURST_A31(src_burst) | + DMA_CHAN_CFG_DST_BURST_A31(dst_burst); +} + +static void sun6i_set_burst_length_h3(u32 *p_cfg, s8 src_burst, s8 dst_burst) +{ + *p_cfg |= DMA_CHAN_CFG_SRC_BURST_H3(src_burst) | + DMA_CHAN_CFG_DST_BURST_H3(dst_burst); +} + static size_t sun6i_get_chan_size(struct sun6i_pchan *pchan) { struct sun6i_desc *txd = pchan->desc; @@ -563,11 +578,11 @@ static int set_config(struct sun6i_dma_dev *sdev, if (dst_width < 0) return dst_width; - *p_cfg = DMA_CHAN_CFG_SRC_BURST(src_burst) | - DMA_CHAN_CFG_SRC_WIDTH(src_width) | - DMA_CHAN_CFG_DST_BURST(dst_burst) | + *p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) | DMA_CHAN_CFG_DST_WIDTH(dst_width); + sdev->cfg->set_burst_length(p_cfg, src_burst, dst_burst); + return 0; } @@ -610,11 +625,11 @@ static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy( DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) | DMA_CHAN_CFG_DST_LINEAR_MODE | DMA_CHAN_CFG_SRC_LINEAR_MODE | - DMA_CHAN_CFG_SRC_BURST(burst) | DMA_CHAN_CFG_SRC_WIDTH(width) | - DMA_CHAN_CFG_DST_BURST(burst) | DMA_CHAN_CFG_DST_WIDTH(width); + sdev->cfg->set_burst_length(v_lli->cfg, burst, burst); + sun6i_dma_lli_add(NULL, v_lli, p_lli, txd); sun6i_dma_dump_lli(vchan, v_lli); @@ -1027,6 +1042,7 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .nr_max_requests = 30, .nr_max_vchans = 53, .clock_autogate_enable = sun6i_enable_clock_autogate_noop; + .set_burst_length = sun6i_set_burst_length_a31; }; /* @@ -1039,6 +1055,7 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_requests = 24, .nr_max_vchans = 37, .clock_autogate_enable = sun6i_enable_clock_autogate_a23; + .set_burst_length = sun6i_set_burst_length_a31; }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { @@ -1046,13 +1063,12 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .nr_max_requests = 28, .nr_max_vchans = 39, .clock_autogate_enable = sun6i_enable_clock_autogate_a23; + .set_burst_length = sun6i_set_burst_length_a31; }; /* * The H3 has 12 physical channels, a maximum DRQ port id of 27, * and a total of 34 usable source and destination endpoints. - * It also supports additional burst lengths and bus widths, - * and the burst length fields have different offsets. */ static struct sun6i_dma_config sun8i_h3_dma_cfg = { @@ -1060,6 +1076,7 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_requests = 27, .nr_max_vchans = 34,
[PATCH v2 04/10] dmaengine: sun6i: Enable additional burst lengths/widths on H3
The H3 supports bursts lengths of 1, 4, 8 and 16 transfers, each with a width of 1, 2, 4 or 8 bytes. The register value for the the width is log2-encoded, change the conversion function to provide the correct value for width == 8. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 54 - 1 file changed, 45 insertions(+), 9 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index 663f4b0b450e..f2ee914cd755 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -120,6 +120,8 @@ struct sun6i_dma_config { void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); u32 src_burst_lengths; u32 dst_burst_lengths; + u32 src_addr_widths; + u32 dst_addr_widths; }; /* @@ -259,8 +261,12 @@ static inline s8 convert_burst(u32 maxburst) switch (maxburst) { case 1: return 0; + case 4: + return 1; case 8: return 2; + case 16: + return 3; default: return -EINVAL; } @@ -268,7 +274,7 @@ static inline s8 convert_burst(u32 maxburst) static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { - return addr_width >> 1; + return ilog2(addr_width); } static void sun6i_enable_clock_autogate_noop(struct sun6i_dma_dev *sdev) @@ -1045,6 +1051,12 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); }; /* @@ -1060,6 +1072,12 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); }; static struct sun6i_dma_config sun8i_a83t_dma_cfg = { @@ -1070,11 +1088,19 @@ static struct sun6i_dma_config sun8i_a83t_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); }; /* * The H3 has 12 physical channels, a maximum DRQ port id of 27, * and a total of 34 usable source and destination endpoints. + * It also supports additional burst lengths and bus widths, + * and the burst length fields have different offsets. */ static struct sun6i_dma_config sun8i_h3_dma_cfg = { @@ -1083,8 +1109,16 @@ static struct sun6i_dma_config sun8i_h3_dma_cfg = { .nr_max_vchans = 34, .clock_autogate_enable = sun6i_enable_clock_autogate_h3; .set_burst_length = sun6i_set_burst_length_h3; - .src_burst_lengths = BIT(1) | BIT(8); - .dst_burst_lengths = BIT(1) | BIT(8); + .src_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .dst_burst_lengths = BIT(1) | BIT(4) | BIT(8) | BIT(16); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); + .dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_8_BYTES); }; /* @@ -1100,6 +1134,12 @@ static struct sun6i_dma_config sun8i_v3s_dma_cfg = { .set_burst_length = sun6i_set_burst_length_a31; .src_burst_lengths = BIT(1) | BIT(8); .dst_burst_lengths = BIT(1) | BIT(8); + .src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | +BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | +BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + .dst_addr_
[PATCH v2 00/10] dmaengine: sun6i: Fixes for H3/A83T, enable A64
Commit 3a03ea763a67 ("dmaengine: sun6i: Add support for Allwinner A83T (sun8i) variant") and commit f008db8c00c1 ("dmaengine: sun6i: Add support for Allwinner H3 (sun8i) variant") added support for the A83T resp. H3, but missed some differences between the original A31 and A83T/H3. The first patch adds a callback to the controller config to set the clock autogating register of different SoC generations, i.e. A31, A23+A83T, H3+later, and uses it to for the correct clock autogating setting. The second patch adds a callback for the burst length setting in the channel config register, which has different field offsets and new burst widths/lengths, which differs between H3 and earlier generations The third patch restructures some code required for the fourth patch and adds the burst lengths to the controller config. The fourth patch adds the burst widths to the config and adds the handling of the H3 specific burst widths. Patch 5 restructures the code to decouple some controller details (e.g. channel count) from the compatible string/the config. Patches 6, 7 and 8 introduce and use the "dma-chans" property for the A64. Although register compatible to the H3, the channel count differs and thus it requires a new compatible. To avoid introduction of new compatibles for each minor variation, anything but the register model is moved to devicetree properties. There is at least one SoC (R40) which can then reuse the A64 compatible, the same would have worked for A83T+V3s. Patches 9 and 10 add the DMA controller node to the devicetree and add the DMA controller reference to the SPI nodes. This patch series could be called v2, but the patches were split and significantly restructured, thus listing changes individually is not to meaningful. Changes in v2: - Use callback for autogating instead of variable for different SoC generations - Use controller specific callback for burst length setting - Store burst lengths in config instead of device structure - Store burst widths in config - Set default number of dma-request if not provided in config or devicetree Stefan Brüns (10): dmaengine: sun6i: Correct setting of clock autogating register for A83T/H3 dmaengine: sun6i: Correct burst length field offsets for H3 dmaengine: sun6i: Restructure code to allow extension for new SoCs dmaengine: sun6i: Enable additional burst lengths/widths on H3 dmaengine: sun6i: Move number of pchans/vchans/request to device struct arm64: allwinner: a64: Add devicetree binding for DMA controller dmaengine: sun6i: Retrieve channel count/max request from devicetree dmaengine: sun6i: Add support for Allwinner A64 and compatibles arm64: allwinner: a64: Add device node for DMA controller arm64: allwinner: a64: add dma controller references to spi nodes .../devicetree/bindings/dma/sun6i-dma.txt | 26 ++ arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 15 ++ drivers/dma/sun6i-dma.c| 265 - 3 files changed, 248 insertions(+), 58 deletions(-) -- 2.14.1
[PATCH v2 03/10] dmaengine: sun6i: Restructure code to allow extension for new SoCs
The current code mixes three distinct operations when transforming the slave config to register settings: 1. special handling of DMA_SLAVE_BUSWIDTH_UNDEFINED, maxburst == 0 2. range checking 3. conversion of raw to register values As the range checks depend on the specific SoC, move these out of the conversion to distinct operations. Signed-off-by: Stefan Brüns --- drivers/dma/sun6i-dma.c | 66 - 1 file changed, 38 insertions(+), 28 deletions(-) diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c index a6fc066a0ac6..663f4b0b450e 100644 --- a/drivers/dma/sun6i-dma.c +++ b/drivers/dma/sun6i-dma.c @@ -118,6 +118,8 @@ struct sun6i_dma_config { */ void (*clock_autogate_enable)(); void (*set_burst_length)(u32 *p_cfg, s8 src_burst, s8 dst_burst); + u32 src_burst_lengths; + u32 dst_burst_lengths; }; /* @@ -266,10 +268,6 @@ static inline s8 convert_burst(u32 maxburst) static inline s8 convert_buswidth(enum dma_slave_buswidth addr_width) { - if ((addr_width < DMA_SLAVE_BUSWIDTH_1_BYTE) || - (addr_width > DMA_SLAVE_BUSWIDTH_4_BYTES)) - return -EINVAL; - return addr_width >> 1; } @@ -542,41 +540,43 @@ static int set_config(struct sun6i_dma_dev *sdev, enum dma_transfer_direction direction, u32 *p_cfg) { + enum dma_slave_buswidth src_addr_width, dst_addr_width; + u32 src_maxburst, dst_maxburst; s8 src_width, dst_width, src_burst, dst_burst; + src_addr_width = sconfig->src_addr_width; + dst_addr_width = sconfig->dst_addr_width; + src_maxburst = sconfig->src_maxburst; + dst_maxburst = sconfig->dst_maxburst; + switch (direction) { case DMA_MEM_TO_DEV: - src_burst = convert_burst(sconfig->src_maxburst ? - sconfig->src_maxburst : 8); - src_width = convert_buswidth(sconfig->src_addr_width != - DMA_SLAVE_BUSWIDTH_UNDEFINED ? - sconfig->src_addr_width : - DMA_SLAVE_BUSWIDTH_4_BYTES); - dst_burst = convert_burst(sconfig->dst_maxburst); - dst_width = convert_buswidth(sconfig->dst_addr_width); + if (src_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + src_maxburst = src_maxburst ? src_maxburst : 8; break; case DMA_DEV_TO_MEM: - src_burst = convert_burst(sconfig->src_maxburst); - src_width = convert_buswidth(sconfig->src_addr_width); - dst_burst = convert_burst(sconfig->dst_maxburst ? - sconfig->dst_maxburst : 8); - dst_width = convert_buswidth(sconfig->dst_addr_width != - DMA_SLAVE_BUSWIDTH_UNDEFINED ? - sconfig->dst_addr_width : - DMA_SLAVE_BUSWIDTH_4_BYTES); + if (dst_addr_width == DMA_SLAVE_BUSWIDTH_UNDEFINED) + dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; + dst_maxburst = dst_maxburst ? dst_maxburst : 8; break; default: return -EINVAL; } - if (src_burst < 0) - return src_burst; - if (src_width < 0) - return src_width; - if (dst_burst < 0) - return dst_burst; - if (dst_width < 0) - return dst_width; + if (!(BIT(src_addr_width) & sdev->slave.src_addr_widths)) + return -EINVAL; + if (!(BIT(dst_addr_width) & sdev->slave.dst_addr_widths)) + return -EINVAL; + if (!(BIT(src_maxburst) & sdev->cfg->src_burst_lengths)) + return -EINVAL; + if (!(BIT(dst_maxburst) & sdev->cfg->dst_burst_lengths)) + return -EINVAL; + + src_width = convert_buswidth(src_addr_width); + dst_width = convert_buswidth(dst_addr_width); + dst_burst = convert_burst(dst_maxburst); + src_burst = convert_burst(src_maxburst); *p_cfg = DMA_CHAN_CFG_SRC_WIDTH(src_width) | DMA_CHAN_CFG_DST_WIDTH(dst_width); @@ -1043,6 +1043,8 @@ static struct sun6i_dma_config sun6i_a31_dma_cfg = { .nr_max_vchans = 53, .clock_autogate_enable = sun6i_enable_clock_autogate_noop; .set_burst_length = sun6i_set_burst_length_a31; + .src_burst_lengths = BIT(1) | BIT(8); + .dst_burst_lengths = BIT(1) | BIT(8); }; /* @@ -1056,6 +1058,8 @@ static struct sun6i_dma_config sun8i_a23_dma_cfg = { .nr_max_vchans = 37, .clock_autogate_enable = sun6i_enable_clock_autog
[PATCH] dmaengine: edma: Reject slave configs using a buswidth of 8 and larger
The driver would happily accept buswidth of 16/32/64 bytes and program garbage to its registers. Signed-off-by: Stefan Brüns --- drivers/dma/edma.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/dma/edma.c b/drivers/dma/edma.c index 3879f80a4815..c944ea339425 100644 --- a/drivers/dma/edma.c +++ b/drivers/dma/edma.c @@ -887,8 +887,8 @@ static int edma_slave_config(struct dma_chan *chan, { struct edma_chan *echan = to_edma_chan(chan); - if (cfg->src_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES || - cfg->dst_addr_width == DMA_SLAVE_BUSWIDTH_8_BYTES) + if (cfg->src_addr_width >= DMA_SLAVE_BUSWIDTH_8_BYTES || + cfg->dst_addr_width >= DMA_SLAVE_BUSWIDTH_8_BYTES) return -EINVAL; memcpy(&echan->cfg, cfg, sizeof(echan->cfg)); -- 2.14.1
[RFC PATCH] dmaengine: sh: Correct src_addr_widths/dst_addr_widths bitmask setting
Obviously, the current value for the burst widths are wrong, and if this value is retrieved from some other subsystem using dma_get_slave_caps, it will wrongly assume burst width of e.g. 3 bytes are supported. Each bit in the bitmask corresponds to a supported width, but it uses an encoding of BIT(), not BIT(log2), as it must be able to encode a width of 3 bytes. The corollary is, it is not possible to encode either a width of 32 or 64 bytes, as the field has a size of 32 bits, and only a subset of the controller capabilities can be exposed. Signed-off-by: Stefan Brüns --- drivers/dma/sh/rcar-dmac.c | 14 ++ drivers/dma/sh/shdmac.c| 13 + drivers/dma/sh/usb-dmac.c | 9 ++--- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c index 2b2c7db3e480..768076caccfd 100644 --- a/drivers/dma/sh/rcar-dmac.c +++ b/drivers/dma/sh/rcar-dmac.c @@ -1734,10 +1734,16 @@ static int rcar_dmac_parse_of(struct device *dev, struct rcar_dmac *dmac) static int rcar_dmac_probe(struct platform_device *pdev) { - const enum dma_slave_buswidth widths = DMA_SLAVE_BUSWIDTH_1_BYTE | - DMA_SLAVE_BUSWIDTH_2_BYTES | DMA_SLAVE_BUSWIDTH_4_BYTES | - DMA_SLAVE_BUSWIDTH_8_BYTES | DMA_SLAVE_BUSWIDTH_16_BYTES | - DMA_SLAVE_BUSWIDTH_32_BYTES | DMA_SLAVE_BUSWIDTH_64_BYTES; + /* +* FIXME: Controller supports DMA_SLAVE_BUSWIDTH_32_BYTES +* and DMA_SLAVE_BUSWIDTH_64_BYTES, +* but this overflows the u32 src/dst_addr_widths fields +*/ + const u32 widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_16_BYTES); unsigned int channels_offset = 0; struct dma_device *engine; struct rcar_dmac *dmac; diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index c94ffab0d25c..bf35b5588b11 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -679,10 +679,15 @@ MODULE_DEVICE_TABLE(of, sh_dmae_of_match); static int sh_dmae_probe(struct platform_device *pdev) { - const enum dma_slave_buswidth widths = - DMA_SLAVE_BUSWIDTH_1_BYTE | DMA_SLAVE_BUSWIDTH_2_BYTES | - DMA_SLAVE_BUSWIDTH_4_BYTES | DMA_SLAVE_BUSWIDTH_8_BYTES | - DMA_SLAVE_BUSWIDTH_16_BYTES | DMA_SLAVE_BUSWIDTH_32_BYTES; + /* +* FIXME: Controller supports DMA_SLAVE_BUSWIDTH_32_BYTES +* but this overflows the u32 src/dst_addr_widths fields +*/ + const u32 widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_16_BYTES); const struct sh_dmae_pdata *pdata; unsigned long chan_flag[SH_DMAE_MAX_CHANNELS] = {}; int chan_irq[SH_DMAE_MAX_CHANNELS]; diff --git a/drivers/dma/sh/usb-dmac.c b/drivers/dma/sh/usb-dmac.c index 31a145154e9f..0ac7e842b70c 100644 --- a/drivers/dma/sh/usb-dmac.c +++ b/drivers/dma/sh/usb-dmac.c @@ -768,7 +768,6 @@ static int usb_dmac_parse_of(struct device *dev, struct usb_dmac *dmac) static int usb_dmac_probe(struct platform_device *pdev) { - const enum dma_slave_buswidth widths = USB_DMAC_SLAVE_BUSWIDTH; struct dma_device *engine; struct usb_dmac *dmac; struct resource *mem; @@ -837,8 +836,12 @@ static int usb_dmac_probe(struct platform_device *pdev) engine->dev = &pdev->dev; - engine->src_addr_widths = widths; - engine->dst_addr_widths = widths; + /* +* FIXME: The controller supports a width of USB_DMAC_SLAVE_BUSWIDTH, +* i.e. 32 bytes, but BIT(32) overflows the u32 bitmask fields. +*/ + engine->src_addr_widths = 0; + engine->dst_addr_widths = 0; engine->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); engine->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; -- 2.14.1
[PATCH 2/4] iio: light: vl6180: Avoid readback of integration time register
Instead of reading the value from the register on each query, store the set value. Signed-off-by: Stefan Brüns --- drivers/iio/light/vl6180.c | 13 - 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 57577d5d18ac..ae768c832d66 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -86,6 +86,7 @@ struct vl6180_data { struct i2c_client *client; struct mutex lock; + unsigned int als_it_ms; }; enum { VL6180_ALS, VL6180_RANGE, VL6180_PROX }; @@ -306,13 +307,11 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; case IIO_CHAN_INFO_INT_TIME: - ret = vl6180_read_word(data->client, VL6180_ALS_IT); - if (ret < 0) - return ret; - *val = 0; /* 1 count = 1ms (0 = 1ms) */ - *val2 = (ret + 1) * 1000; /* convert to seconds */ + *val = 0; + *val2 = data->als_it_ms * 1000; return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_LIGHT: @@ -401,6 +400,9 @@ static int vl6180_set_it(struct vl6180_data *data, int val, int val2) ret = vl6180_write_word(data->client, VL6180_ALS_IT, it_ms - 1); + if (ret >= 0) + data->als_it_ms = it_ms; + fail: vl6180_hold(data, false); mutex_unlock(&data->lock); @@ -472,6 +474,7 @@ static int vl6180_init(struct vl6180_data *data) return ret; /* ALS integration time: 100ms */ + data->als_it_ms = 100; ret = vl6180_write_word(client, VL6180_ALS_IT, VL6180_ALS_IT_100); if (ret < 0) return ret; -- 2.14.1
[PATCH 3/4] iio: light: vl6180: Cleanup als_gain lookup, avoid register readback
Instead of manually iterating the array of allowed gain values, use find_closest. Storing the current gain setting avoids accessing the hardware on each query. Signed-off-by: Stefan Brüns --- drivers/iio/light/vl6180.c | 85 -- 1 file changed, 44 insertions(+), 41 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index ae768c832d66..fcd8ef511bdc 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include @@ -86,6 +87,7 @@ struct vl6180_data { struct i2c_client *client; struct mutex lock; + unsigned int als_gain_milli; unsigned int als_it_ms; }; @@ -276,19 +278,17 @@ static const struct iio_chan_spec vl6180_channels[] = { }; /* - * Columns 3 & 4 represent the same value in decimal and hex notations. - * Kept in order to avoid the datatype conversion while reading the - * hardware_gain. + * Available Ambient Light Sensor gain settings, 1/1000th, and + * corresponding setting for the VL6180_ALS_GAIN register */ -static const int vl6180_als_gain[8][4] = { - { 1,0, 70, VL6180_ALS_GAIN_1 }, - { 1,25, 69, VL6180_ALS_GAIN_1_25 }, - { 1,67, 68, VL6180_ALS_GAIN_1_67 }, - { 2,50, 67, VL6180_ALS_GAIN_2_5 }, - { 5,0, 66, VL6180_ALS_GAIN_5 }, - { 10, 0, 65, VL6180_ALS_GAIN_10 }, - { 20, 0, 64, VL6180_ALS_GAIN_20 }, - { 40, 0, 71, VL6180_ALS_GAIN_40 } +static const int vl6180_als_gain_tab[8] = { + 1000, 1250, 1670, 2500, 5000, 1, 2, 4 +}; +static const u8 vl6180_als_gain_tab_bits[8] = { + VL6180_ALS_GAIN_1,VL6180_ALS_GAIN_1_25, + VL6180_ALS_GAIN_1_67, VL6180_ALS_GAIN_2_5, + VL6180_ALS_GAIN_5,VL6180_ALS_GAIN_10, + VL6180_ALS_GAIN_20, VL6180_ALS_GAIN_40 }; static int vl6180_read_raw(struct iio_dev *indio_dev, @@ -296,7 +296,7 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct vl6180_data *data = iio_priv(indio_dev); - int ret, i; + int ret; switch (mask) { case IIO_CHAN_INFO_RAW: @@ -328,17 +328,11 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_HARDWAREGAIN: - ret = vl6180_read_byte(data->client, VL6180_ALS_GAIN); - if (ret < 0) - return -EINVAL; - for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { - if (ret == vl6180_als_gain[i][2]) { - *val = vl6180_als_gain[i][0]; - *val2 = vl6180_als_gain[i][1]; - } - } + *val = data->als_gain_milli; + *val2 = 1000; + + return IIO_VAL_FRACTIONAL; - return IIO_VAL_INT_PLUS_MICRO; default: return -EINVAL; } @@ -364,25 +358,33 @@ static int vl6180_hold(struct vl6180_data *data, bool hold) static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2) { - int i, ret; - - for (i = 0; i < ARRAY_SIZE(vl6180_als_gain); i++) { - if (val == vl6180_als_gain[i][0] && - val2 == vl6180_als_gain[i][1]) { - mutex_lock(&data->lock); - ret = vl6180_hold(data, true); - if (ret < 0) - goto fail; - ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN, - vl6180_als_gain[i][3]); -fail: - vl6180_hold(data, false); - mutex_unlock(&data->lock); - return ret; - } - } + int i, ret, gain; + + if (val < 1 || val > 40) + return -EINVAL; + + gain = (val * 100 + val2)/1000; + if (gain < 1 || gain > 4) + return -EINVAL; + + i = find_closest(gain, vl6180_als_gain_tab, +ARRAY_SIZE(vl6180_als_gain_tab)); + + mutex_lock(&data->lock); + ret = vl6180_hold(data, true); + if (ret < 0) + goto fail; - return -EINVAL; + ret = vl6180_write_byte(data->client, VL6180_ALS_GAIN, + vl6180_als_gain_tab_bits[i]); + + if (ret >= 0) + data->als_gain_milli = vl6180_als_gain_tab[i]; + +fail: + vl6180_hold(data, false); + mutex_unlock(&data->lock); + return ret; } static int vl6180_set_it(struct vl6180_data *data, int val, int val2) @@ -480,6 +482,7 @@ static int vl618
[PATCH 4/4] iio: light: vl6180: Correct ALS scale for non-default gain/integration time
The reported scale was only correct for the default settings of 100 ms integration time and gain 1. This aligns the reported scale with the behaviour of any other IIO driver and the documented ABI, but may require userspace changes if someone uses non-default settings. Signed-off-by: Stefan Brüns --- drivers/iio/light/vl6180.c | 9 ++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index fcd8ef511bdc..4f7c36d3df5f 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -315,9 +315,12 @@ static int vl6180_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_LIGHT: - *val = 0; /* one ALS count is 0.32 Lux */ - *val2 = 32; - break; + /* one ALS count is 0.32 Lux @ gain 1, IT 100 ms */ + *val = 32000; /* 0.32 * 1000 * 100 */ + *val2 = data->als_gain_milli * data->als_it_ms; + + return IIO_VAL_FRACTIONAL; + case IIO_DISTANCE: *val = 0; /* sensor reports mm, scale to meter */ *val2 = 1000; -- 2.14.1
[PATCH 1/4] iio: light: vl6180: Move range check to integration time setter, cleanup
This improves code uniformity (range checks for als_gain are also done in the setter). Also unmangle rounding and calculation of register value. The calculated integration time it_ms is required in the next patch of the series. Signed-off-by: Stefan Brüns --- drivers/iio/light/vl6180.c | 18 +++--- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c index 6e25b724d941..57577d5d18ac 100644 --- a/drivers/iio/light/vl6180.c +++ b/drivers/iio/light/vl6180.c @@ -386,16 +386,21 @@ static int vl6180_set_als_gain(struct vl6180_data *data, int val, int val2) return -EINVAL; } -static int vl6180_set_it(struct vl6180_data *data, int val2) +static int vl6180_set_it(struct vl6180_data *data, int val, int val2) { - int ret; + int ret, it_ms; + + it_ms = ((val2 + 500) / 1000); /* round to ms */ + if (val != 0 || it_ms < 1 || it_ms > 512) + return -EINVAL; mutex_lock(&data->lock); ret = vl6180_hold(data, true); if (ret < 0) goto fail; - ret = vl6180_write_word(data->client, VL6180_ALS_IT, - (val2 - 500) / 1000); /* write value in ms */ + + ret = vl6180_write_word(data->client, VL6180_ALS_IT, it_ms - 1); + fail: vl6180_hold(data, false); mutex_unlock(&data->lock); @@ -411,15 +416,14 @@ static int vl6180_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_INT_TIME: - if (val != 0 || val2 < 500 || val2 >= 512500) - return -EINVAL; + return vl6180_set_it(data, val, val2); - return vl6180_set_it(data, val2); case IIO_CHAN_INFO_HARDWAREGAIN: if (chan->type != IIO_LIGHT) return -EINVAL; return vl6180_set_als_gain(data, val, val2); + default: return -EINVAL; } -- 2.14.1
[PATCH 0/4] iio: light: vl6180: Several fixes and enhancements
Currently, the vl6180 driver reports bogus in_illuminance_scale values when the integration time or hardware gain are changed from its default value, i.e. it always reports a fixed value. To avoid readback of the register values in case integration time, gain or scale is queried, save the register values. Use the saved values to report the correct scale value. Stefan Brüns (4): iio: light: vl6180: Move range check to integration time setter, cleanup iio: light: vl6180: Avoid readback of integration time register iio: light: vl6180: Cleanup als_gain lookup, avoid register readback iio: light: vl6180: Correct ALS scale for non-default gain/integration time drivers/iio/light/vl6180.c | 125 + 1 file changed, 69 insertions(+), 56 deletions(-) -- 2.14.1