Merge branch 'i2c/for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux
Pull i2c updates from Wolfram Sang: - Peter Rosin did some major rework on the locking of i2c muxes by seperating parent-locked muxes and mux-locked muxes. This avoids deadlocks/workarounds when the mux itself needs i2c commands for muxing. And as a side-effect, other workarounds in the media layer could be eliminated. Also, Peter stepped up as the i2c mux maintainer and will keep an eye on these changes. - major updates to the octeon driver - add a helper to the core to generate the address+rw_bit octal and make drivers use it - quite a bunch of driver updates * 'i2c/for-4.7' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux: (84 commits) i2c: rcar: add DMA support i2c: st: Implement bus clear i2c: only check scl functions when using generic recovery i2c: algo-bit: declare i2c_bit_quirk_no_clk_stretch as static i2c: tegra: disable clock before returning error [media] rtl2832: regmap is aware of lockdep, drop local locking hack [media] rtl2832_sdr: get rid of empty regmap wrappers [media] rtl2832: change the i2c gate to be mux-locked [media] si2168: change the i2c gate to be mux-locked iio: imu: inv_mpu6050: change the i2c gate to be mux-locked i2c: mux: document i2c muxes and elaborate on parent-/mux-locked muxes i2c: mux: relax locking of the top i2c adapter during mux-locked muxing i2c: muxes always lock the parent adapter i2c: allow adapter drivers to override the adapter locking i2c: uniphier: add "\n" at the end of error log i2c: mv64xxx: remove CONFIG_HAVE_CLK conditionals i2c: mv64xxx: use clk_{prepare_enable,disable_unprepare} i2c: mv64xxx: handle probe deferral for the clock i2c: mv64xxx: enable the driver on ARCH_MVEBU i2c: octeon: Add workaround for broken irqs on CN3860 ...
This commit is contained in:
@@ -4,6 +4,12 @@
|
|||||||
|
|
||||||
Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
|
Compatibility with all cn3XXX, cn5XXX and cn6XXX SOCs.
|
||||||
|
|
||||||
|
or
|
||||||
|
|
||||||
|
compatible: "cavium,octeon-7890-twsi"
|
||||||
|
|
||||||
|
Compatibility with cn78XX SOCs.
|
||||||
|
|
||||||
- reg: The base address of the TWSI/I2C bus controller register bank.
|
- reg: The base address of the TWSI/I2C bus controller register bank.
|
||||||
|
|
||||||
- #address-cells: Must be <1>.
|
- #address-cells: Must be <1>.
|
||||||
|
@@ -19,6 +19,9 @@ Optional properties:
|
|||||||
- clock-frequency: desired I2C bus clock frequency in Hz. The absence of this
|
- clock-frequency: desired I2C bus clock frequency in Hz. The absence of this
|
||||||
property indicates the default frequency 100 kHz.
|
property indicates the default frequency 100 kHz.
|
||||||
- clocks: clock specifier.
|
- clocks: clock specifier.
|
||||||
|
- dmas: Must contain a list of two references to DMA specifiers, one for
|
||||||
|
transmission, and one for reception.
|
||||||
|
- dma-names: Must contain a list of two DMA names, "tx" and "rx".
|
||||||
|
|
||||||
- i2c-scl-falling-time-ns: see i2c.txt
|
- i2c-scl-falling-time-ns: see i2c.txt
|
||||||
- i2c-scl-internal-delay-ns: see i2c.txt
|
- i2c-scl-internal-delay-ns: see i2c.txt
|
||||||
|
370
Documentation/i2c/i2c-topology
Normal file
370
Documentation/i2c/i2c-topology
Normal file
@@ -0,0 +1,370 @@
|
|||||||
|
I2C topology
|
||||||
|
============
|
||||||
|
|
||||||
|
There are a couple of reasons for building more complex i2c topologies
|
||||||
|
than a straight-forward i2c bus with one adapter and one or more devices.
|
||||||
|
|
||||||
|
1. A mux may be needed on the bus to prevent address collisions.
|
||||||
|
|
||||||
|
2. The bus may be accessible from some external bus master, and arbitration
|
||||||
|
may be needed to determine if it is ok to access the bus.
|
||||||
|
|
||||||
|
3. A device (particularly RF tuners) may want to avoid the digital noise
|
||||||
|
from the i2c bus, at least most of the time, and sits behind a gate
|
||||||
|
that has to be operated before the device can be accessed.
|
||||||
|
|
||||||
|
Etc
|
||||||
|
|
||||||
|
These constructs are represented as i2c adapter trees by Linux, where
|
||||||
|
each adapter has a parent adapter (except the root adapter) and zero or
|
||||||
|
more child adapters. The root adapter is the actual adapter that issues
|
||||||
|
i2c transfers, and all adapters with a parent are part of an "i2c-mux"
|
||||||
|
object (quoted, since it can also be an arbitrator or a gate).
|
||||||
|
|
||||||
|
Depending of the particular mux driver, something happens when there is
|
||||||
|
an i2c transfer on one of its child adapters. The mux driver can
|
||||||
|
obviously operate a mux, but it can also do arbitration with an external
|
||||||
|
bus master or open a gate. The mux driver has two operations for this,
|
||||||
|
select and deselect. select is called before the transfer and (the
|
||||||
|
optional) deselect is called after the transfer.
|
||||||
|
|
||||||
|
|
||||||
|
Locking
|
||||||
|
=======
|
||||||
|
|
||||||
|
There are two variants of locking available to i2c muxes, they can be
|
||||||
|
mux-locked or parent-locked muxes. As is evident from below, it can be
|
||||||
|
useful to know if a mux is mux-locked or if it is parent-locked. The
|
||||||
|
following list was correct at the time of writing:
|
||||||
|
|
||||||
|
In drivers/i2c/muxes/
|
||||||
|
i2c-arb-gpio-challenge Parent-locked
|
||||||
|
i2c-mux-gpio Normally parent-locked, mux-locked iff
|
||||||
|
all involved gpio pins are controlled by the
|
||||||
|
same i2c root adapter that they mux.
|
||||||
|
i2c-mux-pca9541 Parent-locked
|
||||||
|
i2c-mux-pca954x Parent-locked
|
||||||
|
i2c-mux-pinctrl Normally parent-locked, mux-locked iff
|
||||||
|
all involved pinctrl devices are controlled
|
||||||
|
by the same i2c root adapter that they mux.
|
||||||
|
i2c-mux-reg Parent-locked
|
||||||
|
|
||||||
|
In drivers/iio/
|
||||||
|
imu/inv_mpu6050/ Mux-locked
|
||||||
|
|
||||||
|
In drivers/media/
|
||||||
|
dvb-frontends/m88ds3103 Parent-locked
|
||||||
|
dvb-frontends/rtl2830 Parent-locked
|
||||||
|
dvb-frontends/rtl2832 Mux-locked
|
||||||
|
dvb-frontends/si2168 Mux-locked
|
||||||
|
usb/cx231xx/ Parent-locked
|
||||||
|
|
||||||
|
|
||||||
|
Mux-locked muxes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
Mux-locked muxes does not lock the entire parent adapter during the
|
||||||
|
full select-transfer-deselect transaction, only the muxes on the parent
|
||||||
|
adapter are locked. Mux-locked muxes are mostly interesting if the
|
||||||
|
select and/or deselect operations must use i2c transfers to complete
|
||||||
|
their tasks. Since the parent adapter is not fully locked during the
|
||||||
|
full transaction, unrelated i2c transfers may interleave the different
|
||||||
|
stages of the transaction. This has the benefit that the mux driver
|
||||||
|
may be easier and cleaner to implement, but it has some caveats.
|
||||||
|
|
||||||
|
ML1. If you build a topology with a mux-locked mux being the parent
|
||||||
|
of a parent-locked mux, this might break the expectation from the
|
||||||
|
parent-locked mux that the root adapter is locked during the
|
||||||
|
transaction.
|
||||||
|
|
||||||
|
ML2. It is not safe to build arbitrary topologies with two (or more)
|
||||||
|
mux-locked muxes that are not siblings, when there are address
|
||||||
|
collisions between the devices on the child adapters of these
|
||||||
|
non-sibling muxes.
|
||||||
|
|
||||||
|
I.e. the select-transfer-deselect transaction targeting e.g. device
|
||||||
|
address 0x42 behind mux-one may be interleaved with a similar
|
||||||
|
operation targeting device address 0x42 behind mux-two. The
|
||||||
|
intension with such a topology would in this hypothetical example
|
||||||
|
be that mux-one and mux-two should not be selected simultaneously,
|
||||||
|
but mux-locked muxes do not guarantee that in all topologies.
|
||||||
|
|
||||||
|
ML3. A mux-locked mux cannot be used by a driver for auto-closing
|
||||||
|
gates/muxes, i.e. something that closes automatically after a given
|
||||||
|
number (one, in most cases) of i2c transfers. Unrelated i2c transfers
|
||||||
|
may creep in and close prematurely.
|
||||||
|
|
||||||
|
ML4. If any non-i2c operation in the mux driver changes the i2c mux state,
|
||||||
|
the driver has to lock the root adapter during that operation.
|
||||||
|
Otherwise garbage may appear on the bus as seen from devices
|
||||||
|
behind the mux, when an unrelated i2c transfer is in flight during
|
||||||
|
the non-i2c mux-changing operation.
|
||||||
|
|
||||||
|
|
||||||
|
Mux-locked Example
|
||||||
|
------------------
|
||||||
|
|
||||||
|
.----------. .--------.
|
||||||
|
.--------. | mux- |-----| dev D1 |
|
||||||
|
| root |--+--| locked | '--------'
|
||||||
|
'--------' | | mux M1 |--. .--------.
|
||||||
|
| '----------' '--| dev D2 |
|
||||||
|
| .--------. '--------'
|
||||||
|
'--| dev D3 |
|
||||||
|
'--------'
|
||||||
|
|
||||||
|
When there is an access to D1, this happens:
|
||||||
|
|
||||||
|
1. Someone issues an i2c-transfer to D1.
|
||||||
|
2. M1 locks muxes on its parent (the root adapter in this case).
|
||||||
|
3. M1 calls ->select to ready the mux.
|
||||||
|
4. M1 (presumably) does some i2c-transfers as part of its select.
|
||||||
|
These transfers are normal i2c-transfers that locks the parent
|
||||||
|
adapter.
|
||||||
|
5. M1 feeds the i2c-transfer from step 1 to its parent adapter as a
|
||||||
|
normal i2c-transfer that locks the parent adapter.
|
||||||
|
6. M1 calls ->deselect, if it has one.
|
||||||
|
7. Same rules as in step 4, but for ->deselect.
|
||||||
|
8. M1 unlocks muxes on its parent.
|
||||||
|
|
||||||
|
This means that accesses to D2 are lockout out for the full duration
|
||||||
|
of the entire operation. But accesses to D3 are possibly interleaved
|
||||||
|
at any point.
|
||||||
|
|
||||||
|
|
||||||
|
Parent-locked muxes
|
||||||
|
-------------------
|
||||||
|
|
||||||
|
Parent-locked muxes lock the parent adapter during the full select-
|
||||||
|
transfer-deselect transaction. The implication is that the mux driver
|
||||||
|
has to ensure that any and all i2c transfers through that parent
|
||||||
|
adapter during the transaction are unlocked i2c transfers (using e.g.
|
||||||
|
__i2c_transfer), or a deadlock will follow. There are a couple of
|
||||||
|
caveats.
|
||||||
|
|
||||||
|
PL1. If you build a topology with a parent-locked mux being the child
|
||||||
|
of another mux, this might break a possible assumption from the
|
||||||
|
child mux that the root adapter is unused between its select op
|
||||||
|
and the actual transfer (e.g. if the child mux is auto-closing
|
||||||
|
and the parent mux issus i2c-transfers as part of its select).
|
||||||
|
This is especially the case if the parent mux is mux-locked, but
|
||||||
|
it may also happen if the parent mux is parent-locked.
|
||||||
|
|
||||||
|
PL2. If select/deselect calls out to other subsystems such as gpio,
|
||||||
|
pinctrl, regmap or iio, it is essential that any i2c transfers
|
||||||
|
caused by these subsystems are unlocked. This can be convoluted to
|
||||||
|
accomplish, maybe even impossible if an acceptably clean solution
|
||||||
|
is sought.
|
||||||
|
|
||||||
|
|
||||||
|
Parent-locked Example
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
.----------. .--------.
|
||||||
|
.--------. | parent- |-----| dev D1 |
|
||||||
|
| root |--+--| locked | '--------'
|
||||||
|
'--------' | | mux M1 |--. .--------.
|
||||||
|
| '----------' '--| dev D2 |
|
||||||
|
| .--------. '--------'
|
||||||
|
'--| dev D3 |
|
||||||
|
'--------'
|
||||||
|
|
||||||
|
When there is an access to D1, this happens:
|
||||||
|
|
||||||
|
1. Someone issues an i2c-transfer to D1.
|
||||||
|
2. M1 locks muxes on its parent (the root adapter in this case).
|
||||||
|
3. M1 locks its parent adapter.
|
||||||
|
4. M1 calls ->select to ready the mux.
|
||||||
|
5. If M1 does any i2c-transfers (on this root adapter) as part of
|
||||||
|
its select, those transfers must be unlocked i2c-transfers so
|
||||||
|
that they do not deadlock the root adapter.
|
||||||
|
6. M1 feeds the i2c-transfer from step 1 to the root adapter as an
|
||||||
|
unlocked i2c-transfer, so that it does not deadlock the parent
|
||||||
|
adapter.
|
||||||
|
7. M1 calls ->deselect, if it has one.
|
||||||
|
8. Same rules as in step 5, but for ->deselect.
|
||||||
|
9. M1 unlocks its parent adapter.
|
||||||
|
10. M1 unlocks muxes on its parent.
|
||||||
|
|
||||||
|
|
||||||
|
This means that accesses to both D2 and D3 are locked out for the full
|
||||||
|
duration of the entire operation.
|
||||||
|
|
||||||
|
|
||||||
|
Complex Examples
|
||||||
|
================
|
||||||
|
|
||||||
|
Parent-locked mux as parent of parent-locked mux
|
||||||
|
------------------------------------------------
|
||||||
|
|
||||||
|
This is a useful topology, but it can be bad.
|
||||||
|
|
||||||
|
.----------. .----------. .--------.
|
||||||
|
.--------. | parent- |-----| parent- |-----| dev D1 |
|
||||||
|
| root |--+--| locked | | locked | '--------'
|
||||||
|
'--------' | | mux M1 |--. | mux M2 |--. .--------.
|
||||||
|
| '----------' | '----------' '--| dev D2 |
|
||||||
|
| .--------. | .--------. '--------'
|
||||||
|
'--| dev D4 | '--| dev D3 |
|
||||||
|
'--------' '--------'
|
||||||
|
|
||||||
|
When any device is accessed, all other devices are locked out for
|
||||||
|
the full duration of the operation (both muxes lock their parent,
|
||||||
|
and specifically when M2 requests its parent to lock, M1 passes
|
||||||
|
the buck to the root adapter).
|
||||||
|
|
||||||
|
This topology is bad if M2 is an auto-closing mux and M1->select
|
||||||
|
issues any unlocked i2c transfers on the root adapter that may leak
|
||||||
|
through and be seen by the M2 adapter, thus closing M2 prematurely.
|
||||||
|
|
||||||
|
|
||||||
|
Mux-locked mux as parent of mux-locked mux
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
This is a good topology.
|
||||||
|
|
||||||
|
.----------. .----------. .--------.
|
||||||
|
.--------. | mux- |-----| mux- |-----| dev D1 |
|
||||||
|
| root |--+--| locked | | locked | '--------'
|
||||||
|
'--------' | | mux M1 |--. | mux M2 |--. .--------.
|
||||||
|
| '----------' | '----------' '--| dev D2 |
|
||||||
|
| .--------. | .--------. '--------'
|
||||||
|
'--| dev D4 | '--| dev D3 |
|
||||||
|
'--------' '--------'
|
||||||
|
|
||||||
|
When device D1 is accessed, accesses to D2 are locked out for the
|
||||||
|
full duration of the operation (muxes on the top child adapter of M1
|
||||||
|
are locked). But accesses to D3 and D4 are possibly interleaved at
|
||||||
|
any point. Accesses to D3 locks out D1 and D2, but accesses to D4
|
||||||
|
are still possibly interleaved.
|
||||||
|
|
||||||
|
|
||||||
|
Mux-locked mux as parent of parent-locked mux
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
This is probably a bad topology.
|
||||||
|
|
||||||
|
.----------. .----------. .--------.
|
||||||
|
.--------. | mux- |-----| parent- |-----| dev D1 |
|
||||||
|
| root |--+--| locked | | locked | '--------'
|
||||||
|
'--------' | | mux M1 |--. | mux M2 |--. .--------.
|
||||||
|
| '----------' | '----------' '--| dev D2 |
|
||||||
|
| .--------. | .--------. '--------'
|
||||||
|
'--| dev D4 | '--| dev D3 |
|
||||||
|
'--------' '--------'
|
||||||
|
|
||||||
|
When device D1 is accessed, accesses to D2 and D3 are locked out
|
||||||
|
for the full duration of the operation (M1 locks child muxes on the
|
||||||
|
root adapter). But accesses to D4 are possibly interleaved at any
|
||||||
|
point.
|
||||||
|
|
||||||
|
This kind of topology is generally not suitable and should probably
|
||||||
|
be avoided. The reason is that M2 probably assumes that there will
|
||||||
|
be no i2c transfers during its calls to ->select and ->deselect, and
|
||||||
|
if there are, any such transfers might appear on the slave side of M2
|
||||||
|
as partial i2c transfers, i.e. garbage or worse. This might cause
|
||||||
|
device lockups and/or other problems.
|
||||||
|
|
||||||
|
The topology is especially troublesome if M2 is an auto-closing
|
||||||
|
mux. In that case, any interleaved accesses to D4 might close M2
|
||||||
|
prematurely, as might any i2c-transfers part of M1->select.
|
||||||
|
|
||||||
|
But if M2 is not making the above stated assumption, and if M2 is not
|
||||||
|
auto-closing, the topology is fine.
|
||||||
|
|
||||||
|
|
||||||
|
Parent-locked mux as parent of mux-locked mux
|
||||||
|
---------------------------------------------
|
||||||
|
|
||||||
|
This is a good topology.
|
||||||
|
|
||||||
|
.----------. .----------. .--------.
|
||||||
|
.--------. | parent- |-----| mux- |-----| dev D1 |
|
||||||
|
| root |--+--| locked | | locked | '--------'
|
||||||
|
'--------' | | mux M1 |--. | mux M2 |--. .--------.
|
||||||
|
| '----------' | '----------' '--| dev D2 |
|
||||||
|
| .--------. | .--------. '--------'
|
||||||
|
'--| dev D4 | '--| dev D3 |
|
||||||
|
'--------' '--------'
|
||||||
|
|
||||||
|
When D1 is accessed, accesses to D2 are locked out for the full
|
||||||
|
duration of the operation (muxes on the top child adapter of M1
|
||||||
|
are locked). Accesses to D3 and D4 are possibly interleaved at
|
||||||
|
any point, just as is expected for mux-locked muxes.
|
||||||
|
|
||||||
|
When D3 or D4 are accessed, everything else is locked out. For D3
|
||||||
|
accesses, M1 locks the root adapter. For D4 accesses, the root
|
||||||
|
adapter is locked directly.
|
||||||
|
|
||||||
|
|
||||||
|
Two mux-locked sibling muxes
|
||||||
|
----------------------------
|
||||||
|
|
||||||
|
This is a good topology.
|
||||||
|
|
||||||
|
.--------.
|
||||||
|
.----------. .--| dev D1 |
|
||||||
|
| mux- |--' '--------'
|
||||||
|
.--| locked | .--------.
|
||||||
|
| | mux M1 |-----| dev D2 |
|
||||||
|
| '----------' '--------'
|
||||||
|
| .----------. .--------.
|
||||||
|
.--------. | | mux- |-----| dev D3 |
|
||||||
|
| root |--+--| locked | '--------'
|
||||||
|
'--------' | | mux M2 |--. .--------.
|
||||||
|
| '----------' '--| dev D4 |
|
||||||
|
| .--------. '--------'
|
||||||
|
'--| dev D5 |
|
||||||
|
'--------'
|
||||||
|
|
||||||
|
When D1 is accessed, accesses to D2, D3 and D4 are locked out. But
|
||||||
|
accesses to D5 may be interleaved at any time.
|
||||||
|
|
||||||
|
|
||||||
|
Two parent-locked sibling muxes
|
||||||
|
-------------------------------
|
||||||
|
|
||||||
|
This is a good topology.
|
||||||
|
|
||||||
|
.--------.
|
||||||
|
.----------. .--| dev D1 |
|
||||||
|
| parent- |--' '--------'
|
||||||
|
.--| locked | .--------.
|
||||||
|
| | mux M1 |-----| dev D2 |
|
||||||
|
| '----------' '--------'
|
||||||
|
| .----------. .--------.
|
||||||
|
.--------. | | parent- |-----| dev D3 |
|
||||||
|
| root |--+--| locked | '--------'
|
||||||
|
'--------' | | mux M2 |--. .--------.
|
||||||
|
| '----------' '--| dev D4 |
|
||||||
|
| .--------. '--------'
|
||||||
|
'--| dev D5 |
|
||||||
|
'--------'
|
||||||
|
|
||||||
|
When any device is accessed, accesses to all other devices are locked
|
||||||
|
out.
|
||||||
|
|
||||||
|
|
||||||
|
Mux-locked and parent-locked sibling muxes
|
||||||
|
------------------------------------------
|
||||||
|
|
||||||
|
This is a good topology.
|
||||||
|
|
||||||
|
.--------.
|
||||||
|
.----------. .--| dev D1 |
|
||||||
|
| mux- |--' '--------'
|
||||||
|
.--| locked | .--------.
|
||||||
|
| | mux M1 |-----| dev D2 |
|
||||||
|
| '----------' '--------'
|
||||||
|
| .----------. .--------.
|
||||||
|
.--------. | | parent- |-----| dev D3 |
|
||||||
|
| root |--+--| locked | '--------'
|
||||||
|
'--------' | | mux M2 |--. .--------.
|
||||||
|
| '----------' '--| dev D4 |
|
||||||
|
| .--------. '--------'
|
||||||
|
'--| dev D5 |
|
||||||
|
'--------'
|
||||||
|
|
||||||
|
When D1 or D2 are accessed, accesses to D3 and D4 are locked out while
|
||||||
|
accesses to D5 may interleave. When D3 or D4 are accessed, accesses to
|
||||||
|
all other devices are locked out.
|
@@ -5348,6 +5348,7 @@ I2C MUXES
|
|||||||
M: Peter Rosin <peda@axentia.se>
|
M: Peter Rosin <peda@axentia.se>
|
||||||
L: linux-i2c@vger.kernel.org
|
L: linux-i2c@vger.kernel.org
|
||||||
S: Maintained
|
S: Maintained
|
||||||
|
F: Documentation/i2c/i2c-topology
|
||||||
F: Documentation/i2c/muxes/
|
F: Documentation/i2c/muxes/
|
||||||
F: Documentation/devicetree/bindings/i2c/i2c-mux*
|
F: Documentation/devicetree/bindings/i2c/i2c-mux*
|
||||||
F: drivers/i2c/i2c-mux.c
|
F: drivers/i2c/i2c-mux.c
|
||||||
|
@@ -617,7 +617,7 @@ const struct i2c_algorithm i2c_bit_algo = {
|
|||||||
};
|
};
|
||||||
EXPORT_SYMBOL(i2c_bit_algo);
|
EXPORT_SYMBOL(i2c_bit_algo);
|
||||||
|
|
||||||
const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = {
|
static const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = {
|
||||||
.flags = I2C_AQ_NO_CLK_STRETCH,
|
.flags = I2C_AQ_NO_CLK_STRETCH,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -663,7 +663,7 @@ config I2C_MT65XX
|
|||||||
|
|
||||||
config I2C_MV64XXX
|
config I2C_MV64XXX
|
||||||
tristate "Marvell mv64xxx I2C Controller"
|
tristate "Marvell mv64xxx I2C Controller"
|
||||||
depends on MV64X60 || PLAT_ORION || ARCH_SUNXI
|
depends on MV64X60 || PLAT_ORION || ARCH_SUNXI || ARCH_MVEBU
|
||||||
help
|
help
|
||||||
If you say yes to this option, support will be included for the
|
If you say yes to this option, support will be included for the
|
||||||
built-in I2C interface on the Marvell 64xxx line of host bridges.
|
built-in I2C interface on the Marvell 64xxx line of host bridges.
|
||||||
|
@@ -267,7 +267,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
|
|||||||
iproc_i2c->msg = msg;
|
iproc_i2c->msg = msg;
|
||||||
|
|
||||||
/* format and load slave address into the TX FIFO */
|
/* format and load slave address into the TX FIFO */
|
||||||
addr = msg->addr << 1 | (msg->flags & I2C_M_RD ? 1 : 0);
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
writel(addr, iproc_i2c->base + M_TX_OFFSET);
|
writel(addr, iproc_i2c->base + M_TX_OFFSET);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -501,10 +501,7 @@ static int bcm_kona_i2c_do_addr(struct bcm_kona_i2c_dev *dev,
|
|||||||
return -EREMOTEIO;
|
return -EREMOTEIO;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addr = msg->addr << 1;
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
|
|
||||||
if (msg->flags & I2C_M_RD)
|
|
||||||
addr |= 1;
|
|
||||||
|
|
||||||
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
|
if (bcm_kona_i2c_write_byte(dev, addr, 0) < 0)
|
||||||
return -EREMOTEIO;
|
return -EREMOTEIO;
|
||||||
|
@@ -446,9 +446,7 @@ static int brcmstb_i2c_do_addr(struct brcmstb_i2c_dev *dev,
|
|||||||
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
addr = msg->addr << 1;
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
if (msg->flags & I2C_M_RD)
|
|
||||||
addr |= 1;
|
|
||||||
|
|
||||||
bsc_writel(dev, addr, chip_address);
|
bsc_writel(dev, addr, chip_address);
|
||||||
}
|
}
|
||||||
|
@@ -197,9 +197,7 @@ static void cpm_i2c_parse_message(struct i2c_adapter *adap,
|
|||||||
tbdf = cpm->tbase + tx;
|
tbdf = cpm->tbase + tx;
|
||||||
rbdf = cpm->rbase + rx;
|
rbdf = cpm->rbase + rx;
|
||||||
|
|
||||||
addr = pmsg->addr << 1;
|
addr = i2c_8bit_addr_from_msg(pmsg);
|
||||||
if (pmsg->flags & I2C_M_RD)
|
|
||||||
addr |= 1;
|
|
||||||
|
|
||||||
tb = cpm->txbuf[tx];
|
tb = cpm->txbuf[tx];
|
||||||
rb = cpm->rxbuf[rx];
|
rb = cpm->rxbuf[rx];
|
||||||
|
@@ -19,6 +19,7 @@
|
|||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/mfd/dln2.h>
|
#include <linux/mfd/dln2.h>
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
|
||||||
#define DLN2_I2C_MODULE_ID 0x03
|
#define DLN2_I2C_MODULE_ID 0x03
|
||||||
#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID)
|
#define DLN2_I2C_CMD(cmd) DLN2_CMD(cmd, DLN2_I2C_MODULE_ID)
|
||||||
@@ -210,6 +211,7 @@ static int dln2_i2c_probe(struct platform_device *pdev)
|
|||||||
dln2->adapter.algo = &dln2_i2c_usb_algorithm;
|
dln2->adapter.algo = &dln2_i2c_usb_algorithm;
|
||||||
dln2->adapter.quirks = &dln2_i2c_quirks;
|
dln2->adapter.quirks = &dln2_i2c_quirks;
|
||||||
dln2->adapter.dev.parent = dev;
|
dln2->adapter.dev.parent = dev;
|
||||||
|
ACPI_COMPANION_SET(&dln2->adapter.dev, ACPI_COMPANION(&pdev->dev));
|
||||||
dln2->adapter.dev.of_node = dev->of_node;
|
dln2->adapter.dev.of_node = dev->of_node;
|
||||||
i2c_set_adapdata(&dln2->adapter, dln2);
|
i2c_set_adapdata(&dln2->adapter, dln2);
|
||||||
snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d",
|
snprintf(dln2->adapter.name, sizeof(dln2->adapter.name), "%s-%s-%d",
|
||||||
|
@@ -861,14 +861,8 @@ static int exynos5_i2c_resume_noirq(struct device *dev)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
|
static const struct dev_pm_ops exynos5_i2c_dev_pm_ops = {
|
||||||
#ifdef CONFIG_PM_SLEEP
|
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(exynos5_i2c_suspend_noirq,
|
||||||
.suspend_noirq = exynos5_i2c_suspend_noirq,
|
exynos5_i2c_resume_noirq)
|
||||||
.resume_noirq = exynos5_i2c_resume_noirq,
|
|
||||||
.freeze_noirq = exynos5_i2c_suspend_noirq,
|
|
||||||
.thaw_noirq = exynos5_i2c_resume_noirq,
|
|
||||||
.poweroff_noirq = exynos5_i2c_suspend_noirq,
|
|
||||||
.restore_noirq = exynos5_i2c_resume_noirq,
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct platform_driver exynos5_i2c_driver = {
|
static struct platform_driver exynos5_i2c_driver = {
|
||||||
|
@@ -94,6 +94,7 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/platform_data/itco_wdt.h>
|
#include <linux/platform_data/itco_wdt.h>
|
||||||
|
#include <linux/pm_runtime.h>
|
||||||
|
|
||||||
#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
|
#if (defined CONFIG_I2C_MUX_GPIO || defined CONFIG_I2C_MUX_GPIO_MODULE) && \
|
||||||
defined CONFIG_DMI
|
defined CONFIG_DMI
|
||||||
@@ -714,9 +715,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
|||||||
{
|
{
|
||||||
int hwpec;
|
int hwpec;
|
||||||
int block = 0;
|
int block = 0;
|
||||||
int ret, xact = 0;
|
int ret = 0, xact = 0;
|
||||||
struct i801_priv *priv = i2c_get_adapdata(adap);
|
struct i801_priv *priv = i2c_get_adapdata(adap);
|
||||||
|
|
||||||
|
pm_runtime_get_sync(&priv->pci_dev->dev);
|
||||||
|
|
||||||
hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
|
hwpec = (priv->features & FEATURE_SMBUS_PEC) && (flags & I2C_CLIENT_PEC)
|
||||||
&& size != I2C_SMBUS_QUICK
|
&& size != I2C_SMBUS_QUICK
|
||||||
&& size != I2C_SMBUS_I2C_BLOCK_DATA;
|
&& size != I2C_SMBUS_I2C_BLOCK_DATA;
|
||||||
@@ -773,7 +776,8 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
|||||||
default:
|
default:
|
||||||
dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n",
|
dev_err(&priv->pci_dev->dev, "Unsupported transaction %d\n",
|
||||||
size);
|
size);
|
||||||
return -EOPNOTSUPP;
|
ret = -EOPNOTSUPP;
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hwpec) /* enable/disable hardware PEC */
|
if (hwpec) /* enable/disable hardware PEC */
|
||||||
@@ -796,11 +800,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
|||||||
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
|
~(SMBAUXCTL_CRC | SMBAUXCTL_E32B), SMBAUXCTL(priv));
|
||||||
|
|
||||||
if (block)
|
if (block)
|
||||||
return ret;
|
goto out;
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
goto out;
|
||||||
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
|
if ((read_write == I2C_SMBUS_WRITE) || (xact == I801_QUICK))
|
||||||
return 0;
|
goto out;
|
||||||
|
|
||||||
switch (xact & 0x7f) {
|
switch (xact & 0x7f) {
|
||||||
case I801_BYTE: /* Result put in SMBHSTDAT0 */
|
case I801_BYTE: /* Result put in SMBHSTDAT0 */
|
||||||
@@ -812,7 +816,11 @@ static s32 i801_access(struct i2c_adapter *adap, u16 addr,
|
|||||||
(inb_p(SMBHSTDAT1(priv)) << 8);
|
(inb_p(SMBHSTDAT1(priv)) << 8);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
|
out:
|
||||||
|
pm_runtime_mark_last_busy(&priv->pci_dev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&priv->pci_dev->dev);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1413,6 +1421,11 @@ static int i801_probe(struct pci_dev *dev, const struct pci_device_id *id)
|
|||||||
|
|
||||||
pci_set_drvdata(dev, priv);
|
pci_set_drvdata(dev, priv);
|
||||||
|
|
||||||
|
pm_runtime_set_autosuspend_delay(&dev->dev, 1000);
|
||||||
|
pm_runtime_use_autosuspend(&dev->dev);
|
||||||
|
pm_runtime_put_autosuspend(&dev->dev);
|
||||||
|
pm_runtime_allow(&dev->dev);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1420,6 +1433,9 @@ static void i801_remove(struct pci_dev *dev)
|
|||||||
{
|
{
|
||||||
struct i801_priv *priv = pci_get_drvdata(dev);
|
struct i801_priv *priv = pci_get_drvdata(dev);
|
||||||
|
|
||||||
|
pm_runtime_forbid(&dev->dev);
|
||||||
|
pm_runtime_get_noresume(&dev->dev);
|
||||||
|
|
||||||
i801_del_mux(priv);
|
i801_del_mux(priv);
|
||||||
i2c_del_adapter(&priv->adapter);
|
i2c_del_adapter(&priv->adapter);
|
||||||
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
|
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
|
||||||
@@ -1433,34 +1449,32 @@ static void i801_remove(struct pci_dev *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static int i801_suspend(struct pci_dev *dev, pm_message_t mesg)
|
static int i801_suspend(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i801_priv *priv = pci_get_drvdata(dev);
|
struct pci_dev *pci_dev = to_pci_dev(dev);
|
||||||
|
struct i801_priv *priv = pci_get_drvdata(pci_dev);
|
||||||
|
|
||||||
pci_save_state(dev);
|
pci_write_config_byte(pci_dev, SMBHSTCFG, priv->original_hstcfg);
|
||||||
pci_write_config_byte(dev, SMBHSTCFG, priv->original_hstcfg);
|
|
||||||
pci_set_power_state(dev, pci_choose_state(dev, mesg));
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i801_resume(struct pci_dev *dev)
|
static int i801_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
pci_set_power_state(dev, PCI_D0);
|
|
||||||
pci_restore_state(dev);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
#define i801_suspend NULL
|
|
||||||
#define i801_resume NULL
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static UNIVERSAL_DEV_PM_OPS(i801_pm_ops, i801_suspend,
|
||||||
|
i801_resume, NULL);
|
||||||
|
|
||||||
static struct pci_driver i801_driver = {
|
static struct pci_driver i801_driver = {
|
||||||
.name = "i801_smbus",
|
.name = "i801_smbus",
|
||||||
.id_table = i801_ids,
|
.id_table = i801_ids,
|
||||||
.probe = i801_probe,
|
.probe = i801_probe,
|
||||||
.remove = i801_remove,
|
.remove = i801_remove,
|
||||||
.suspend = i801_suspend,
|
.driver = {
|
||||||
.resume = i801_resume,
|
.pm = &i801_pm_ops,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init i2c_i801_init(void)
|
static int __init i2c_i801_init(void)
|
||||||
|
@@ -269,7 +269,7 @@ static int iic_smbus_quick(struct ibm_iic_private* dev, const struct i2c_msg* p)
|
|||||||
ndelay(t->hd_sta);
|
ndelay(t->hd_sta);
|
||||||
|
|
||||||
/* Send address */
|
/* Send address */
|
||||||
v = (u8)((p->addr << 1) | ((p->flags & I2C_M_RD) ? 1 : 0));
|
v = i2c_8bit_addr_from_msg(p);
|
||||||
for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){
|
for (i = 0, mask = 0x80; i < 8; ++i, mask >>= 1){
|
||||||
out_8(&iic->directcntl, sda);
|
out_8(&iic->directcntl, sda);
|
||||||
ndelay(t->low / 2);
|
ndelay(t->low / 2);
|
||||||
|
@@ -751,9 +751,7 @@ static unsigned int img_i2c_atomic(struct img_i2c *i2c,
|
|||||||
switch (i2c->at_cur_cmd) {
|
switch (i2c->at_cur_cmd) {
|
||||||
case CMD_GEN_START:
|
case CMD_GEN_START:
|
||||||
next_cmd = CMD_GEN_DATA;
|
next_cmd = CMD_GEN_DATA;
|
||||||
next_data = (i2c->msg.addr << 1);
|
next_data = i2c_8bit_addr_from_msg(&i2c->msg);
|
||||||
if (i2c->msg.flags & I2C_M_RD)
|
|
||||||
next_data |= 0x1;
|
|
||||||
break;
|
break;
|
||||||
case CMD_GEN_DATA:
|
case CMD_GEN_DATA:
|
||||||
if (i2c->line_status & LINESTAT_INPUT_HELD_V)
|
if (i2c->line_status & LINESTAT_INPUT_HELD_V)
|
||||||
|
@@ -525,7 +525,7 @@ static int i2c_imx_start(struct imx_i2c_struct *i2c_imx)
|
|||||||
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR);
|
imx_i2c_write_reg(i2c_imx->hwdata->i2cr_ien_opcode, i2c_imx, IMX_I2C_I2CR);
|
||||||
|
|
||||||
/* Wait controller to be stable */
|
/* Wait controller to be stable */
|
||||||
udelay(50);
|
usleep_range(50, 150);
|
||||||
|
|
||||||
/* Start I2C transaction */
|
/* Start I2C transaction */
|
||||||
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR);
|
||||||
|
@@ -50,10 +50,7 @@ iic_cook_addr(struct i2c_msg *msg)
|
|||||||
{
|
{
|
||||||
unsigned char addr;
|
unsigned char addr;
|
||||||
|
|
||||||
addr = (msg->addr << 1);
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
|
|
||||||
if (msg->flags & I2C_M_RD)
|
|
||||||
addr |= 1;
|
|
||||||
|
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
@@ -133,9 +133,7 @@ static void i2c_lpc2k_pump_msg(struct lpc2k_i2c *i2c)
|
|||||||
case M_START:
|
case M_START:
|
||||||
case M_REPSTART:
|
case M_REPSTART:
|
||||||
/* Start bit was just sent out, send out addr and dir */
|
/* Start bit was just sent out, send out addr and dir */
|
||||||
data = i2c->msg->addr << 1;
|
data = i2c_8bit_addr_from_msg(i2c->msg);
|
||||||
if (i2c->msg->flags & I2C_M_RD)
|
|
||||||
data |= 1;
|
|
||||||
|
|
||||||
writel(data, i2c->base + LPC24XX_I2DAT);
|
writel(data, i2c->base + LPC24XX_I2DAT);
|
||||||
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
|
writel(LPC24XX_STA, i2c->base + LPC24XX_I2CONCLR);
|
||||||
|
@@ -413,10 +413,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
|
|||||||
else
|
else
|
||||||
writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
|
writew(I2C_FS_START_CON, i2c->base + OFFSET_EXT_CONF);
|
||||||
|
|
||||||
addr_reg = msgs->addr << 1;
|
addr_reg = i2c_8bit_addr_from_msg(msgs);
|
||||||
if (i2c->op == I2C_MASTER_RD)
|
|
||||||
addr_reg |= 0x1;
|
|
||||||
|
|
||||||
writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR);
|
writew(addr_reg, i2c->base + OFFSET_SLAVE_ADDR);
|
||||||
|
|
||||||
/* Clear interrupt status */
|
/* Clear interrupt status */
|
||||||
|
@@ -134,9 +134,7 @@ struct mv64xxx_i2c_data {
|
|||||||
int rc;
|
int rc;
|
||||||
u32 freq_m;
|
u32 freq_m;
|
||||||
u32 freq_n;
|
u32 freq_n;
|
||||||
#if defined(CONFIG_HAVE_CLK)
|
|
||||||
struct clk *clk;
|
struct clk *clk;
|
||||||
#endif
|
|
||||||
wait_queue_head_t waitq;
|
wait_queue_head_t waitq;
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
struct i2c_msg *msg;
|
struct i2c_msg *msg;
|
||||||
@@ -757,7 +755,6 @@ static const struct of_device_id mv64xxx_i2c_of_match_table[] = {
|
|||||||
MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
|
MODULE_DEVICE_TABLE(of, mv64xxx_i2c_of_match_table);
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
#ifdef CONFIG_HAVE_CLK
|
|
||||||
static int
|
static int
|
||||||
mv64xxx_calc_freq(struct mv64xxx_i2c_data *drv_data,
|
mv64xxx_calc_freq(struct mv64xxx_i2c_data *drv_data,
|
||||||
const int tclk, const int n, const int m)
|
const int tclk, const int n, const int m)
|
||||||
@@ -791,25 +788,20 @@ mv64xxx_find_baud_factors(struct mv64xxx_i2c_data *drv_data,
|
|||||||
return false;
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
#endif /* CONFIG_HAVE_CLK */
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
||||||
struct device *dev)
|
struct device *dev)
|
||||||
{
|
{
|
||||||
/* CLK is mandatory when using DT to describe the i2c bus. We
|
|
||||||
* need to know tclk in order to calculate bus clock
|
|
||||||
* factors.
|
|
||||||
*/
|
|
||||||
#if !defined(CONFIG_HAVE_CLK)
|
|
||||||
/* Have OF but no CLK */
|
|
||||||
return -ENODEV;
|
|
||||||
#else
|
|
||||||
const struct of_device_id *device;
|
const struct of_device_id *device;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
u32 bus_freq, tclk;
|
u32 bus_freq, tclk;
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
|
|
||||||
|
/* CLK is mandatory when using DT to describe the i2c bus. We
|
||||||
|
* need to know tclk in order to calculate bus clock
|
||||||
|
* factors.
|
||||||
|
*/
|
||||||
if (IS_ERR(drv_data->clk)) {
|
if (IS_ERR(drv_data->clk)) {
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto out;
|
goto out;
|
||||||
@@ -869,7 +861,6 @@ mv64xxx_of_config(struct mv64xxx_i2c_data *drv_data,
|
|||||||
|
|
||||||
out:
|
out:
|
||||||
return rc;
|
return rc;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
#else /* CONFIG_OF */
|
#else /* CONFIG_OF */
|
||||||
static int
|
static int
|
||||||
@@ -907,14 +898,13 @@ mv64xxx_i2c_probe(struct platform_device *pd)
|
|||||||
init_waitqueue_head(&drv_data->waitq);
|
init_waitqueue_head(&drv_data->waitq);
|
||||||
spin_lock_init(&drv_data->lock);
|
spin_lock_init(&drv_data->lock);
|
||||||
|
|
||||||
#if defined(CONFIG_HAVE_CLK)
|
|
||||||
/* Not all platforms have a clk */
|
/* Not all platforms have a clk */
|
||||||
drv_data->clk = devm_clk_get(&pd->dev, NULL);
|
drv_data->clk = devm_clk_get(&pd->dev, NULL);
|
||||||
if (!IS_ERR(drv_data->clk)) {
|
if (IS_ERR(drv_data->clk) && PTR_ERR(drv_data->clk) == -EPROBE_DEFER)
|
||||||
clk_prepare(drv_data->clk);
|
return -EPROBE_DEFER;
|
||||||
clk_enable(drv_data->clk);
|
if (!IS_ERR(drv_data->clk))
|
||||||
}
|
clk_prepare_enable(drv_data->clk);
|
||||||
#endif
|
|
||||||
if (pdata) {
|
if (pdata) {
|
||||||
drv_data->freq_m = pdata->freq_m;
|
drv_data->freq_m = pdata->freq_m;
|
||||||
drv_data->freq_n = pdata->freq_n;
|
drv_data->freq_n = pdata->freq_n;
|
||||||
@@ -964,13 +954,10 @@ exit_reset:
|
|||||||
if (!IS_ERR_OR_NULL(drv_data->rstc))
|
if (!IS_ERR_OR_NULL(drv_data->rstc))
|
||||||
reset_control_assert(drv_data->rstc);
|
reset_control_assert(drv_data->rstc);
|
||||||
exit_clk:
|
exit_clk:
|
||||||
#if defined(CONFIG_HAVE_CLK)
|
|
||||||
/* Not all platforms have a clk */
|
/* Not all platforms have a clk */
|
||||||
if (!IS_ERR(drv_data->clk)) {
|
if (!IS_ERR(drv_data->clk))
|
||||||
clk_disable(drv_data->clk);
|
clk_disable_unprepare(drv_data->clk);
|
||||||
clk_unprepare(drv_data->clk);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -983,13 +970,9 @@ mv64xxx_i2c_remove(struct platform_device *dev)
|
|||||||
free_irq(drv_data->irq, drv_data);
|
free_irq(drv_data->irq, drv_data);
|
||||||
if (!IS_ERR_OR_NULL(drv_data->rstc))
|
if (!IS_ERR_OR_NULL(drv_data->rstc))
|
||||||
reset_control_assert(drv_data->rstc);
|
reset_control_assert(drv_data->rstc);
|
||||||
#if defined(CONFIG_HAVE_CLK)
|
|
||||||
/* Not all platforms have a clk */
|
/* Not all platforms have a clk */
|
||||||
if (!IS_ERR(drv_data->clk)) {
|
if (!IS_ERR(drv_data->clk))
|
||||||
clk_disable(drv_data->clk);
|
clk_disable_unprepare(drv_data->clk);
|
||||||
clk_unprepare(drv_data->clk);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -127,7 +127,7 @@ static struct pci_driver nforce2_driver;
|
|||||||
|
|
||||||
/* For multiplexing support, we need a global reference to the 1st
|
/* For multiplexing support, we need a global reference to the 1st
|
||||||
SMBus channel */
|
SMBus channel */
|
||||||
#if defined CONFIG_I2C_NFORCE2_S4985 || defined CONFIG_I2C_NFORCE2_S4985_MODULE
|
#if IS_ENABLED(CONFIG_I2C_NFORCE2_S4985)
|
||||||
struct i2c_adapter *nforce2_smbus;
|
struct i2c_adapter *nforce2_smbus;
|
||||||
EXPORT_SYMBOL_GPL(nforce2_smbus);
|
EXPORT_SYMBOL_GPL(nforce2_smbus);
|
||||||
|
|
||||||
|
@@ -178,10 +178,7 @@ static void ocores_process(struct ocores_i2c *i2c)
|
|||||||
if (i2c->nmsgs) { /* end? */
|
if (i2c->nmsgs) { /* end? */
|
||||||
/* send start? */
|
/* send start? */
|
||||||
if (!(msg->flags & I2C_M_NOSTART)) {
|
if (!(msg->flags & I2C_M_NOSTART)) {
|
||||||
u8 addr = (msg->addr << 1);
|
u8 addr = i2c_8bit_addr_from_msg(msg);
|
||||||
|
|
||||||
if (msg->flags & I2C_M_RD)
|
|
||||||
addr |= 1;
|
|
||||||
|
|
||||||
i2c->state = STATE_START;
|
i2c->state = STATE_START;
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -185,7 +185,6 @@ enum {
|
|||||||
#define OMAP_I2C_IP_V2_INTERRUPTS_MASK 0x6FFF
|
#define OMAP_I2C_IP_V2_INTERRUPTS_MASK 0x6FFF
|
||||||
|
|
||||||
struct omap_i2c_dev {
|
struct omap_i2c_dev {
|
||||||
spinlock_t lock; /* IRQ synchronization */
|
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
void __iomem *base; /* virtual */
|
void __iomem *base; /* virtual */
|
||||||
int irq;
|
int irq;
|
||||||
@@ -995,15 +994,12 @@ omap_i2c_isr(int irq, void *dev_id)
|
|||||||
u16 mask;
|
u16 mask;
|
||||||
u16 stat;
|
u16 stat;
|
||||||
|
|
||||||
spin_lock(&omap->lock);
|
|
||||||
mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
|
|
||||||
stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
|
stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
|
||||||
|
mask = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
|
||||||
|
|
||||||
if (stat & mask)
|
if (stat & mask)
|
||||||
ret = IRQ_WAKE_THREAD;
|
ret = IRQ_WAKE_THREAD;
|
||||||
|
|
||||||
spin_unlock(&omap->lock);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1011,12 +1007,10 @@ static irqreturn_t
|
|||||||
omap_i2c_isr_thread(int this_irq, void *dev_id)
|
omap_i2c_isr_thread(int this_irq, void *dev_id)
|
||||||
{
|
{
|
||||||
struct omap_i2c_dev *omap = dev_id;
|
struct omap_i2c_dev *omap = dev_id;
|
||||||
unsigned long flags;
|
|
||||||
u16 bits;
|
u16 bits;
|
||||||
u16 stat;
|
u16 stat;
|
||||||
int err = 0, count = 0;
|
int err = 0, count = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&omap->lock, flags);
|
|
||||||
do {
|
do {
|
||||||
bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
|
bits = omap_i2c_read_reg(omap, OMAP_I2C_IE_REG);
|
||||||
stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
|
stat = omap_i2c_read_reg(omap, OMAP_I2C_STAT_REG);
|
||||||
@@ -1142,8 +1136,6 @@ omap_i2c_isr_thread(int this_irq, void *dev_id)
|
|||||||
omap_i2c_complete_cmd(omap, err);
|
omap_i2c_complete_cmd(omap, err);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
spin_unlock_irqrestore(&omap->lock, flags);
|
|
||||||
|
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1330,8 +1322,6 @@ omap_i2c_probe(struct platform_device *pdev)
|
|||||||
omap->dev = &pdev->dev;
|
omap->dev = &pdev->dev;
|
||||||
omap->irq = irq;
|
omap->irq = irq;
|
||||||
|
|
||||||
spin_lock_init(&omap->lock);
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, omap);
|
platform_set_drvdata(pdev, omap);
|
||||||
init_completion(&omap->cmd_complete);
|
init_completion(&omap->cmd_complete);
|
||||||
|
|
||||||
|
@@ -150,13 +150,11 @@ static int i2c_powermac_master_xfer( struct i2c_adapter *adap,
|
|||||||
{
|
{
|
||||||
struct pmac_i2c_bus *bus = i2c_get_adapdata(adap);
|
struct pmac_i2c_bus *bus = i2c_get_adapdata(adap);
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
int read;
|
|
||||||
int addrdir;
|
int addrdir;
|
||||||
|
|
||||||
if (msgs->flags & I2C_M_TEN)
|
if (msgs->flags & I2C_M_TEN)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
read = (msgs->flags & I2C_M_RD) != 0;
|
addrdir = i2c_8bit_addr_from_msg(msgs);
|
||||||
addrdir = (msgs->addr << 1) | read;
|
|
||||||
|
|
||||||
rc = pmac_i2c_open(bus, 0);
|
rc = pmac_i2c_open(bus, 0);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
|
@@ -515,7 +515,7 @@ static int qup_i2c_get_data_len(struct qup_i2c_dev *qup)
|
|||||||
static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
|
static int qup_i2c_set_tags(u8 *tags, struct qup_i2c_dev *qup,
|
||||||
struct i2c_msg *msg, int is_dma)
|
struct i2c_msg *msg, int is_dma)
|
||||||
{
|
{
|
||||||
u16 addr = (msg->addr << 1) | ((msg->flags & I2C_M_RD) == I2C_M_RD);
|
u16 addr = i2c_8bit_addr_from_msg(msg);
|
||||||
int len = 0;
|
int len = 0;
|
||||||
int data_len;
|
int data_len;
|
||||||
|
|
||||||
|
@@ -21,6 +21,8 @@
|
|||||||
*/
|
*/
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
#include <linux/dmaengine.h>
|
||||||
|
#include <linux/dma-mapping.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/interrupt.h>
|
#include <linux/interrupt.h>
|
||||||
#include <linux/io.h>
|
#include <linux/io.h>
|
||||||
@@ -43,6 +45,8 @@
|
|||||||
#define ICSAR 0x1C /* slave address */
|
#define ICSAR 0x1C /* slave address */
|
||||||
#define ICMAR 0x20 /* master address */
|
#define ICMAR 0x20 /* master address */
|
||||||
#define ICRXTX 0x24 /* data port */
|
#define ICRXTX 0x24 /* data port */
|
||||||
|
#define ICDMAER 0x3c /* DMA enable */
|
||||||
|
#define ICFBSCR 0x38 /* first bit setup cycle */
|
||||||
|
|
||||||
/* ICSCR */
|
/* ICSCR */
|
||||||
#define SDBS (1 << 3) /* slave data buffer select */
|
#define SDBS (1 << 3) /* slave data buffer select */
|
||||||
@@ -78,6 +82,16 @@
|
|||||||
#define MDR (1 << 1)
|
#define MDR (1 << 1)
|
||||||
#define MAT (1 << 0) /* slave addr xfer done */
|
#define MAT (1 << 0) /* slave addr xfer done */
|
||||||
|
|
||||||
|
/* ICDMAER */
|
||||||
|
#define RSDMAE (1 << 3) /* DMA Slave Received Enable */
|
||||||
|
#define TSDMAE (1 << 2) /* DMA Slave Transmitted Enable */
|
||||||
|
#define RMDMAE (1 << 1) /* DMA Master Received Enable */
|
||||||
|
#define TMDMAE (1 << 0) /* DMA Master Transmitted Enable */
|
||||||
|
|
||||||
|
/* ICFBSCR */
|
||||||
|
#define TCYC06 0x04 /* 6*Tcyc delay 1st bit between SDA and SCL */
|
||||||
|
#define TCYC17 0x0f /* 17*Tcyc delay 1st bit between SDA and SCL */
|
||||||
|
|
||||||
|
|
||||||
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
|
#define RCAR_BUS_PHASE_START (MDBS | MIE | ESG)
|
||||||
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
|
#define RCAR_BUS_PHASE_DATA (MDBS | MIE)
|
||||||
@@ -120,6 +134,12 @@ struct rcar_i2c_priv {
|
|||||||
u32 flags;
|
u32 flags;
|
||||||
enum rcar_i2c_type devtype;
|
enum rcar_i2c_type devtype;
|
||||||
struct i2c_client *slave;
|
struct i2c_client *slave;
|
||||||
|
|
||||||
|
struct resource *res;
|
||||||
|
struct dma_chan *dma_tx;
|
||||||
|
struct dma_chan *dma_rx;
|
||||||
|
struct scatterlist sg;
|
||||||
|
enum dma_data_direction dma_direction;
|
||||||
};
|
};
|
||||||
|
|
||||||
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
#define rcar_i2c_priv_to_dev(p) ((p)->adap.dev.parent)
|
||||||
@@ -287,6 +307,118 @@ static void rcar_i2c_next_msg(struct rcar_i2c_priv *priv)
|
|||||||
/*
|
/*
|
||||||
* interrupt functions
|
* interrupt functions
|
||||||
*/
|
*/
|
||||||
|
static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
|
||||||
|
{
|
||||||
|
struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
|
||||||
|
? priv->dma_rx : priv->dma_tx;
|
||||||
|
|
||||||
|
/* Disable DMA Master Received/Transmitted */
|
||||||
|
rcar_i2c_write(priv, ICDMAER, 0);
|
||||||
|
|
||||||
|
/* Reset default delay */
|
||||||
|
rcar_i2c_write(priv, ICFBSCR, TCYC06);
|
||||||
|
|
||||||
|
dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
|
||||||
|
priv->msg->len, priv->dma_direction);
|
||||||
|
|
||||||
|
priv->dma_direction = DMA_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv)
|
||||||
|
{
|
||||||
|
if (priv->dma_direction == DMA_NONE)
|
||||||
|
return;
|
||||||
|
else if (priv->dma_direction == DMA_FROM_DEVICE)
|
||||||
|
dmaengine_terminate_all(priv->dma_rx);
|
||||||
|
else if (priv->dma_direction == DMA_TO_DEVICE)
|
||||||
|
dmaengine_terminate_all(priv->dma_tx);
|
||||||
|
|
||||||
|
rcar_i2c_dma_unmap(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rcar_i2c_dma_callback(void *data)
|
||||||
|
{
|
||||||
|
struct rcar_i2c_priv *priv = data;
|
||||||
|
|
||||||
|
priv->pos += sg_dma_len(&priv->sg);
|
||||||
|
|
||||||
|
rcar_i2c_dma_unmap(priv);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rcar_i2c_dma(struct rcar_i2c_priv *priv)
|
||||||
|
{
|
||||||
|
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||||
|
struct i2c_msg *msg = priv->msg;
|
||||||
|
bool read = msg->flags & I2C_M_RD;
|
||||||
|
enum dma_data_direction dir = read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
|
||||||
|
struct dma_chan *chan = read ? priv->dma_rx : priv->dma_tx;
|
||||||
|
struct dma_async_tx_descriptor *txdesc;
|
||||||
|
dma_addr_t dma_addr;
|
||||||
|
dma_cookie_t cookie;
|
||||||
|
unsigned char *buf;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
/* Do not use DMA if it's not available or for messages < 8 bytes */
|
||||||
|
if (IS_ERR(chan) || msg->len < 8)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (read) {
|
||||||
|
/*
|
||||||
|
* The last two bytes needs to be fetched using PIO in
|
||||||
|
* order for the STOP phase to work.
|
||||||
|
*/
|
||||||
|
buf = priv->msg->buf;
|
||||||
|
len = priv->msg->len - 2;
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* First byte in message was sent using PIO.
|
||||||
|
*/
|
||||||
|
buf = priv->msg->buf + 1;
|
||||||
|
len = priv->msg->len - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
|
||||||
|
if (dma_mapping_error(dev, dma_addr)) {
|
||||||
|
dev_dbg(dev, "dma map failed, using PIO\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
sg_dma_len(&priv->sg) = len;
|
||||||
|
sg_dma_address(&priv->sg) = dma_addr;
|
||||||
|
|
||||||
|
priv->dma_direction = dir;
|
||||||
|
|
||||||
|
txdesc = dmaengine_prep_slave_sg(chan, &priv->sg, 1,
|
||||||
|
read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV,
|
||||||
|
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
|
||||||
|
if (!txdesc) {
|
||||||
|
dev_dbg(dev, "dma prep slave sg failed, using PIO\n");
|
||||||
|
rcar_i2c_cleanup_dma(priv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
txdesc->callback = rcar_i2c_dma_callback;
|
||||||
|
txdesc->callback_param = priv;
|
||||||
|
|
||||||
|
cookie = dmaengine_submit(txdesc);
|
||||||
|
if (dma_submit_error(cookie)) {
|
||||||
|
dev_dbg(dev, "submitting dma failed, using PIO\n");
|
||||||
|
rcar_i2c_cleanup_dma(priv);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set delay for DMA operations */
|
||||||
|
rcar_i2c_write(priv, ICFBSCR, TCYC17);
|
||||||
|
|
||||||
|
/* Enable DMA Master Received/Transmitted */
|
||||||
|
if (read)
|
||||||
|
rcar_i2c_write(priv, ICDMAER, RMDMAE);
|
||||||
|
else
|
||||||
|
rcar_i2c_write(priv, ICDMAER, TMDMAE);
|
||||||
|
|
||||||
|
dma_async_issue_pending(chan);
|
||||||
|
}
|
||||||
|
|
||||||
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
||||||
{
|
{
|
||||||
struct i2c_msg *msg = priv->msg;
|
struct i2c_msg *msg = priv->msg;
|
||||||
@@ -306,6 +438,12 @@ static void rcar_i2c_irq_send(struct rcar_i2c_priv *priv, u32 msr)
|
|||||||
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
|
rcar_i2c_write(priv, ICRXTX, msg->buf[priv->pos]);
|
||||||
priv->pos++;
|
priv->pos++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Try to use DMA to transmit the rest of the data if
|
||||||
|
* address transfer pashe just finished.
|
||||||
|
*/
|
||||||
|
if (msr & MAT)
|
||||||
|
rcar_i2c_dma(priv);
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* The last data was pushed to ICRXTX on _PREV_ empty irq.
|
* The last data was pushed to ICRXTX on _PREV_ empty irq.
|
||||||
@@ -340,7 +478,11 @@ static void rcar_i2c_irq_recv(struct rcar_i2c_priv *priv, u32 msr)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (msr & MAT) {
|
if (msr & MAT) {
|
||||||
/* Address transfer phase finished, but no data at this point. */
|
/*
|
||||||
|
* Address transfer phase finished, but no data at this point.
|
||||||
|
* Try to use DMA to receive data.
|
||||||
|
*/
|
||||||
|
rcar_i2c_dma(priv);
|
||||||
} else if (priv->pos < msg->len) {
|
} else if (priv->pos < msg->len) {
|
||||||
/* get received data */
|
/* get received data */
|
||||||
msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
|
msg->buf[priv->pos] = rcar_i2c_read(priv, ICRXTX);
|
||||||
@@ -472,6 +614,81 @@ out:
|
|||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct dma_chan *rcar_i2c_request_dma_chan(struct device *dev,
|
||||||
|
enum dma_transfer_direction dir,
|
||||||
|
dma_addr_t port_addr)
|
||||||
|
{
|
||||||
|
struct dma_chan *chan;
|
||||||
|
struct dma_slave_config cfg;
|
||||||
|
char *chan_name = dir == DMA_MEM_TO_DEV ? "tx" : "rx";
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
chan = dma_request_slave_channel_reason(dev, chan_name);
|
||||||
|
if (IS_ERR(chan)) {
|
||||||
|
ret = PTR_ERR(chan);
|
||||||
|
dev_dbg(dev, "request_channel failed for %s (%d)\n",
|
||||||
|
chan_name, ret);
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(&cfg, 0, sizeof(cfg));
|
||||||
|
cfg.direction = dir;
|
||||||
|
if (dir == DMA_MEM_TO_DEV) {
|
||||||
|
cfg.dst_addr = port_addr;
|
||||||
|
cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
} else {
|
||||||
|
cfg.src_addr = port_addr;
|
||||||
|
cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = dmaengine_slave_config(chan, &cfg);
|
||||||
|
if (ret) {
|
||||||
|
dev_dbg(dev, "slave_config failed for %s (%d)\n",
|
||||||
|
chan_name, ret);
|
||||||
|
dma_release_channel(chan);
|
||||||
|
return ERR_PTR(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
dev_dbg(dev, "got DMA channel for %s\n", chan_name);
|
||||||
|
return chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rcar_i2c_request_dma(struct rcar_i2c_priv *priv,
|
||||||
|
struct i2c_msg *msg)
|
||||||
|
{
|
||||||
|
struct device *dev = rcar_i2c_priv_to_dev(priv);
|
||||||
|
bool read;
|
||||||
|
struct dma_chan *chan;
|
||||||
|
enum dma_transfer_direction dir;
|
||||||
|
|
||||||
|
read = msg->flags & I2C_M_RD;
|
||||||
|
|
||||||
|
chan = read ? priv->dma_rx : priv->dma_tx;
|
||||||
|
if (PTR_ERR(chan) != -EPROBE_DEFER)
|
||||||
|
return;
|
||||||
|
|
||||||
|
dir = read ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
|
||||||
|
chan = rcar_i2c_request_dma_chan(dev, dir, priv->res->start + ICRXTX);
|
||||||
|
|
||||||
|
if (read)
|
||||||
|
priv->dma_rx = chan;
|
||||||
|
else
|
||||||
|
priv->dma_tx = chan;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void rcar_i2c_release_dma(struct rcar_i2c_priv *priv)
|
||||||
|
{
|
||||||
|
if (!IS_ERR(priv->dma_tx)) {
|
||||||
|
dma_release_channel(priv->dma_tx);
|
||||||
|
priv->dma_tx = ERR_PTR(-EPROBE_DEFER);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IS_ERR(priv->dma_rx)) {
|
||||||
|
dma_release_channel(priv->dma_rx);
|
||||||
|
priv->dma_rx = ERR_PTR(-EPROBE_DEFER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
||||||
struct i2c_msg *msgs,
|
struct i2c_msg *msgs,
|
||||||
int num)
|
int num)
|
||||||
@@ -493,6 +710,7 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
|||||||
ret = -EOPNOTSUPP;
|
ret = -EOPNOTSUPP;
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
rcar_i2c_request_dma(priv, msgs + i);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* init first message */
|
/* init first message */
|
||||||
@@ -504,6 +722,7 @@ static int rcar_i2c_master_xfer(struct i2c_adapter *adap,
|
|||||||
time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE,
|
time_left = wait_event_timeout(priv->wait, priv->flags & ID_DONE,
|
||||||
num * adap->timeout);
|
num * adap->timeout);
|
||||||
if (!time_left) {
|
if (!time_left) {
|
||||||
|
rcar_i2c_cleanup_dma(priv);
|
||||||
rcar_i2c_init(priv);
|
rcar_i2c_init(priv);
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
} else if (priv->flags & ID_NACK) {
|
} else if (priv->flags & ID_NACK) {
|
||||||
@@ -591,7 +810,6 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||||||
{
|
{
|
||||||
struct rcar_i2c_priv *priv;
|
struct rcar_i2c_priv *priv;
|
||||||
struct i2c_adapter *adap;
|
struct i2c_adapter *adap;
|
||||||
struct resource *res;
|
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct i2c_timings i2c_t;
|
struct i2c_timings i2c_t;
|
||||||
int irq, ret;
|
int irq, ret;
|
||||||
@@ -606,8 +824,9 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(priv->clk);
|
return PTR_ERR(priv->clk);
|
||||||
}
|
}
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
priv->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
priv->io = devm_ioremap_resource(dev, res);
|
|
||||||
|
priv->io = devm_ioremap_resource(dev, priv->res);
|
||||||
if (IS_ERR(priv->io))
|
if (IS_ERR(priv->io))
|
||||||
return PTR_ERR(priv->io);
|
return PTR_ERR(priv->io);
|
||||||
|
|
||||||
@@ -626,6 +845,11 @@ static int rcar_i2c_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
i2c_parse_fw_timings(dev, &i2c_t, false);
|
i2c_parse_fw_timings(dev, &i2c_t, false);
|
||||||
|
|
||||||
|
/* Init DMA */
|
||||||
|
sg_init_table(&priv->sg, 1);
|
||||||
|
priv->dma_direction = DMA_NONE;
|
||||||
|
priv->dma_rx = priv->dma_tx = ERR_PTR(-EPROBE_DEFER);
|
||||||
|
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
pm_runtime_get_sync(dev);
|
pm_runtime_get_sync(dev);
|
||||||
ret = rcar_i2c_clock_calculate(priv, &i2c_t);
|
ret = rcar_i2c_clock_calculate(priv, &i2c_t);
|
||||||
@@ -673,6 +897,7 @@ static int rcar_i2c_remove(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
|
|
||||||
i2c_del_adapter(&priv->adap);
|
i2c_del_adapter(&priv->adap);
|
||||||
|
rcar_i2c_release_dma(priv);
|
||||||
if (priv->flags & ID_P_PM_BLOCKED)
|
if (priv->flags & ID_P_PM_BLOCKED)
|
||||||
pm_runtime_put(dev);
|
pm_runtime_put(dev);
|
||||||
pm_runtime_disable(dev);
|
pm_runtime_disable(dev);
|
||||||
|
@@ -101,10 +101,7 @@ struct rk3x_i2c {
|
|||||||
struct notifier_block clk_rate_nb;
|
struct notifier_block clk_rate_nb;
|
||||||
|
|
||||||
/* Settings */
|
/* Settings */
|
||||||
unsigned int scl_frequency;
|
struct i2c_timings t;
|
||||||
unsigned int scl_rise_ns;
|
|
||||||
unsigned int scl_fall_ns;
|
|
||||||
unsigned int sda_fall_ns;
|
|
||||||
|
|
||||||
/* Synchronization & notification */
|
/* Synchronization & notification */
|
||||||
spinlock_t lock;
|
spinlock_t lock;
|
||||||
@@ -437,10 +434,7 @@ out:
|
|||||||
* Calculate divider values for desired SCL frequency
|
* Calculate divider values for desired SCL frequency
|
||||||
*
|
*
|
||||||
* @clk_rate: I2C input clock rate
|
* @clk_rate: I2C input clock rate
|
||||||
* @scl_rate: Desired SCL rate
|
* @t: Known I2C timing information.
|
||||||
* @scl_rise_ns: How many ns it takes for SCL to rise.
|
|
||||||
* @scl_fall_ns: How many ns it takes for SCL to fall.
|
|
||||||
* @sda_fall_ns: How many ns it takes for SDA to fall.
|
|
||||||
* @div_low: Divider output for low
|
* @div_low: Divider output for low
|
||||||
* @div_high: Divider output for high
|
* @div_high: Divider output for high
|
||||||
*
|
*
|
||||||
@@ -448,11 +442,10 @@ out:
|
|||||||
* a best-effort divider value is returned in divs. If the target rate is
|
* a best-effort divider value is returned in divs. If the target rate is
|
||||||
* too high, we silently use the highest possible rate.
|
* too high, we silently use the highest possible rate.
|
||||||
*/
|
*/
|
||||||
static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
static int rk3x_i2c_calc_divs(unsigned long clk_rate,
|
||||||
unsigned long scl_rise_ns,
|
struct i2c_timings *t,
|
||||||
unsigned long scl_fall_ns,
|
unsigned long *div_low,
|
||||||
unsigned long sda_fall_ns,
|
unsigned long *div_high)
|
||||||
unsigned long *div_low, unsigned long *div_high)
|
|
||||||
{
|
{
|
||||||
unsigned long spec_min_low_ns, spec_min_high_ns;
|
unsigned long spec_min_low_ns, spec_min_high_ns;
|
||||||
unsigned long spec_setup_start, spec_max_data_hold_ns;
|
unsigned long spec_setup_start, spec_max_data_hold_ns;
|
||||||
@@ -472,12 +465,12 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
|||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Only support standard-mode and fast-mode */
|
/* Only support standard-mode and fast-mode */
|
||||||
if (WARN_ON(scl_rate > 400000))
|
if (WARN_ON(t->bus_freq_hz > 400000))
|
||||||
scl_rate = 400000;
|
t->bus_freq_hz = 400000;
|
||||||
|
|
||||||
/* prevent scl_rate_khz from becoming 0 */
|
/* prevent scl_rate_khz from becoming 0 */
|
||||||
if (WARN_ON(scl_rate < 1000))
|
if (WARN_ON(t->bus_freq_hz < 1000))
|
||||||
scl_rate = 1000;
|
t->bus_freq_hz = 1000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* min_low_ns: The minimum number of ns we need to hold low to
|
* min_low_ns: The minimum number of ns we need to hold low to
|
||||||
@@ -491,7 +484,7 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
|||||||
* This is because the i2c host on Rockchip holds the data line
|
* This is because the i2c host on Rockchip holds the data line
|
||||||
* for half the low time.
|
* for half the low time.
|
||||||
*/
|
*/
|
||||||
if (scl_rate <= 100000) {
|
if (t->bus_freq_hz <= 100000) {
|
||||||
/* Standard-mode */
|
/* Standard-mode */
|
||||||
spec_min_low_ns = 4700;
|
spec_min_low_ns = 4700;
|
||||||
spec_setup_start = 4700;
|
spec_setup_start = 4700;
|
||||||
@@ -506,7 +499,7 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
|||||||
spec_max_data_hold_ns = 900;
|
spec_max_data_hold_ns = 900;
|
||||||
data_hold_buffer_ns = 50;
|
data_hold_buffer_ns = 50;
|
||||||
}
|
}
|
||||||
min_high_ns = scl_rise_ns + spec_min_high_ns;
|
min_high_ns = t->scl_rise_ns + spec_min_high_ns;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Timings for repeated start:
|
* Timings for repeated start:
|
||||||
@@ -517,18 +510,18 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
|||||||
* we meet tSU;STA and tHD;STA times.
|
* we meet tSU;STA and tHD;STA times.
|
||||||
*/
|
*/
|
||||||
min_high_ns = max(min_high_ns,
|
min_high_ns = max(min_high_ns,
|
||||||
DIV_ROUND_UP((scl_rise_ns + spec_setup_start) * 1000, 875));
|
DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start) * 1000, 875));
|
||||||
min_high_ns = max(min_high_ns,
|
min_high_ns = max(min_high_ns,
|
||||||
DIV_ROUND_UP((scl_rise_ns + spec_setup_start +
|
DIV_ROUND_UP((t->scl_rise_ns + spec_setup_start +
|
||||||
sda_fall_ns + spec_min_high_ns), 2));
|
t->sda_fall_ns + spec_min_high_ns), 2));
|
||||||
|
|
||||||
min_low_ns = scl_fall_ns + spec_min_low_ns;
|
min_low_ns = t->scl_fall_ns + spec_min_low_ns;
|
||||||
max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns;
|
max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns;
|
||||||
min_total_ns = min_low_ns + min_high_ns;
|
min_total_ns = min_low_ns + min_high_ns;
|
||||||
|
|
||||||
/* Adjust to avoid overflow */
|
/* Adjust to avoid overflow */
|
||||||
clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
|
clk_rate_khz = DIV_ROUND_UP(clk_rate, 1000);
|
||||||
scl_rate_khz = scl_rate / 1000;
|
scl_rate_khz = t->bus_freq_hz / 1000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We need the total div to be >= this number
|
* We need the total div to be >= this number
|
||||||
@@ -616,14 +609,13 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate,
|
|||||||
|
|
||||||
static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
|
static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
|
||||||
{
|
{
|
||||||
|
struct i2c_timings *t = &i2c->t;
|
||||||
unsigned long div_low, div_high;
|
unsigned long div_low, div_high;
|
||||||
u64 t_low_ns, t_high_ns;
|
u64 t_low_ns, t_high_ns;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns,
|
ret = rk3x_i2c_calc_divs(clk_rate, t, &div_low, &div_high);
|
||||||
i2c->scl_fall_ns, i2c->sda_fall_ns,
|
WARN_ONCE(ret != 0, "Could not reach SCL freq %u", t->bus_freq_hz);
|
||||||
&div_low, &div_high);
|
|
||||||
WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency);
|
|
||||||
|
|
||||||
clk_enable(i2c->clk);
|
clk_enable(i2c->clk);
|
||||||
i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
|
i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
|
||||||
@@ -634,7 +626,7 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate)
|
|||||||
dev_dbg(i2c->dev,
|
dev_dbg(i2c->dev,
|
||||||
"CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
|
"CLK %lukhz, Req %uns, Act low %lluns high %lluns\n",
|
||||||
clk_rate / 1000,
|
clk_rate / 1000,
|
||||||
1000000000 / i2c->scl_frequency,
|
1000000000 / t->bus_freq_hz,
|
||||||
t_low_ns, t_high_ns);
|
t_low_ns, t_high_ns);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,9 +656,7 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long
|
|||||||
|
|
||||||
switch (event) {
|
switch (event) {
|
||||||
case PRE_RATE_CHANGE:
|
case PRE_RATE_CHANGE:
|
||||||
if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency,
|
if (rk3x_i2c_calc_divs(ndata->new_rate, &i2c->t,
|
||||||
i2c->scl_rise_ns, i2c->scl_fall_ns,
|
|
||||||
i2c->sda_fall_ns,
|
|
||||||
&div_low, &div_high) != 0)
|
&div_low, &div_high) != 0)
|
||||||
return NOTIFY_STOP;
|
return NOTIFY_STOP;
|
||||||
|
|
||||||
@@ -880,37 +870,8 @@ static int rk3x_i2c_probe(struct platform_device *pdev)
|
|||||||
match = of_match_node(rk3x_i2c_match, np);
|
match = of_match_node(rk3x_i2c_match, np);
|
||||||
i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data;
|
i2c->soc_data = (struct rk3x_i2c_soc_data *)match->data;
|
||||||
|
|
||||||
if (of_property_read_u32(pdev->dev.of_node, "clock-frequency",
|
/* use common interface to get I2C timing properties */
|
||||||
&i2c->scl_frequency)) {
|
i2c_parse_fw_timings(&pdev->dev, &i2c->t, true);
|
||||||
dev_info(&pdev->dev, "using default SCL frequency: %d\n",
|
|
||||||
DEFAULT_SCL_RATE);
|
|
||||||
i2c->scl_frequency = DEFAULT_SCL_RATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i2c->scl_frequency == 0 || i2c->scl_frequency > 400 * 1000) {
|
|
||||||
dev_warn(&pdev->dev, "invalid SCL frequency specified.\n");
|
|
||||||
dev_warn(&pdev->dev, "using default SCL frequency: %d\n",
|
|
||||||
DEFAULT_SCL_RATE);
|
|
||||||
i2c->scl_frequency = DEFAULT_SCL_RATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Read rise and fall time from device tree. If not available use
|
|
||||||
* the default maximum timing from the specification.
|
|
||||||
*/
|
|
||||||
if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-rising-time-ns",
|
|
||||||
&i2c->scl_rise_ns)) {
|
|
||||||
if (i2c->scl_frequency <= 100000)
|
|
||||||
i2c->scl_rise_ns = 1000;
|
|
||||||
else
|
|
||||||
i2c->scl_rise_ns = 300;
|
|
||||||
}
|
|
||||||
if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-falling-time-ns",
|
|
||||||
&i2c->scl_fall_ns))
|
|
||||||
i2c->scl_fall_ns = 300;
|
|
||||||
if (of_property_read_u32(pdev->dev.of_node, "i2c-sda-falling-time-ns",
|
|
||||||
&i2c->sda_fall_ns))
|
|
||||||
i2c->sda_fall_ns = i2c->scl_fall_ns;
|
|
||||||
|
|
||||||
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
|
strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name));
|
||||||
i2c->adap.owner = THIS_MODULE;
|
i2c->adap.owner = THIS_MODULE;
|
||||||
|
@@ -163,15 +163,14 @@ static const struct of_device_id s3c24xx_i2c_match[] = {
|
|||||||
MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
|
MODULE_DEVICE_TABLE(of, s3c24xx_i2c_match);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* s3c24xx_get_device_quirks
|
/*
|
||||||
*
|
|
||||||
* Get controller type either from device tree or platform device variant.
|
* Get controller type either from device tree or platform device variant.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev)
|
static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
if (pdev->dev.of_node) {
|
if (pdev->dev.of_node) {
|
||||||
const struct of_device_id *match;
|
const struct of_device_id *match;
|
||||||
|
|
||||||
match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
|
match = of_match_node(s3c24xx_i2c_match, pdev->dev.of_node);
|
||||||
return (kernel_ulong_t)match->data;
|
return (kernel_ulong_t)match->data;
|
||||||
}
|
}
|
||||||
@@ -179,12 +178,10 @@ static inline kernel_ulong_t s3c24xx_get_device_quirks(struct platform_device *p
|
|||||||
return platform_get_device_id(pdev)->driver_data;
|
return platform_get_device_id(pdev)->driver_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_master_complete
|
/*
|
||||||
*
|
* Complete the message and wake up the caller, using the given return code,
|
||||||
* complete the message and wake up the caller, using the given return code,
|
|
||||||
* or zero to mean ok.
|
* or zero to mean ok.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)
|
static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)
|
||||||
{
|
{
|
||||||
dev_dbg(i2c->dev, "master_complete %d\n", ret);
|
dev_dbg(i2c->dev, "master_complete %d\n", ret);
|
||||||
@@ -217,7 +214,6 @@ static inline void s3c24xx_i2c_enable_ack(struct s3c24xx_i2c *i2c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* irq enable/disable functions */
|
/* irq enable/disable functions */
|
||||||
|
|
||||||
static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)
|
static inline void s3c24xx_i2c_disable_irq(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
unsigned long tmp;
|
unsigned long tmp;
|
||||||
@@ -251,11 +247,9 @@ static bool is_ack(struct s3c24xx_i2c *i2c)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_message_start
|
/*
|
||||||
*
|
|
||||||
* put the start of a message onto the bus
|
* put the start of a message onto the bus
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
|
static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
|
||||||
struct i2c_msg *msg)
|
struct i2c_msg *msg)
|
||||||
{
|
{
|
||||||
@@ -284,9 +278,10 @@ static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
|
|||||||
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
|
dev_dbg(i2c->dev, "START: %08lx to IICSTAT, %02x to DS\n", stat, addr);
|
||||||
writeb(addr, i2c->regs + S3C2410_IICDS);
|
writeb(addr, i2c->regs + S3C2410_IICDS);
|
||||||
|
|
||||||
/* delay here to ensure the data byte has gotten onto the bus
|
/*
|
||||||
* before the transaction is started */
|
* delay here to ensure the data byte has gotten onto the bus
|
||||||
|
* before the transaction is started
|
||||||
|
*/
|
||||||
ndelay(i2c->tx_setup);
|
ndelay(i2c->tx_setup);
|
||||||
|
|
||||||
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
|
dev_dbg(i2c->dev, "iiccon, %08lx\n", iiccon);
|
||||||
@@ -361,50 +356,46 @@ static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)
|
|||||||
s3c24xx_i2c_disable_irq(i2c);
|
s3c24xx_i2c_disable_irq(i2c);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* helper functions to determine the current state in the set of
|
/*
|
||||||
* messages we are sending */
|
* helper functions to determine the current state in the set of
|
||||||
|
* messages we are sending
|
||||||
|
*/
|
||||||
|
|
||||||
/* is_lastmsg()
|
/*
|
||||||
*
|
|
||||||
* returns TRUE if the current message is the last in the set
|
* returns TRUE if the current message is the last in the set
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline int is_lastmsg(struct s3c24xx_i2c *i2c)
|
static inline int is_lastmsg(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
return i2c->msg_idx >= (i2c->msg_num - 1);
|
return i2c->msg_idx >= (i2c->msg_num - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* is_msglast
|
/*
|
||||||
*
|
|
||||||
* returns TRUE if we this is the last byte in the current message
|
* returns TRUE if we this is the last byte in the current message
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline int is_msglast(struct s3c24xx_i2c *i2c)
|
static inline int is_msglast(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
/* msg->len is always 1 for the first byte of smbus block read.
|
/*
|
||||||
|
* msg->len is always 1 for the first byte of smbus block read.
|
||||||
* Actual length will be read from slave. More bytes will be
|
* Actual length will be read from slave. More bytes will be
|
||||||
* read according to the length then. */
|
* read according to the length then.
|
||||||
|
*/
|
||||||
if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1)
|
if (i2c->msg->flags & I2C_M_RECV_LEN && i2c->msg->len == 1)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return i2c->msg_ptr == i2c->msg->len-1;
|
return i2c->msg_ptr == i2c->msg->len-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* is_msgend
|
/*
|
||||||
*
|
|
||||||
* returns TRUE if we reached the end of the current message
|
* returns TRUE if we reached the end of the current message
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline int is_msgend(struct s3c24xx_i2c *i2c)
|
static inline int is_msgend(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
return i2c->msg_ptr >= i2c->msg->len;
|
return i2c->msg_ptr >= i2c->msg->len;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* i2c_s3c_irq_nextbyte
|
/*
|
||||||
*
|
|
||||||
* process an interrupt and work out what to do
|
* process an interrupt and work out what to do
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
||||||
{
|
{
|
||||||
unsigned long tmp;
|
unsigned long tmp;
|
||||||
@@ -423,14 +414,13 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
goto out_ack;
|
goto out_ack;
|
||||||
|
|
||||||
case STATE_START:
|
case STATE_START:
|
||||||
/* last thing we did was send a start condition on the
|
/*
|
||||||
|
* last thing we did was send a start condition on the
|
||||||
* bus, or started a new i2c message
|
* bus, or started a new i2c message
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (iicstat & S3C2410_IICSTAT_LASTBIT &&
|
if (iicstat & S3C2410_IICSTAT_LASTBIT &&
|
||||||
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
|
!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
|
||||||
/* ack was not received... */
|
/* ack was not received... */
|
||||||
|
|
||||||
dev_dbg(i2c->dev, "ack was not received\n");
|
dev_dbg(i2c->dev, "ack was not received\n");
|
||||||
s3c24xx_i2c_stop(i2c, -ENXIO);
|
s3c24xx_i2c_stop(i2c, -ENXIO);
|
||||||
goto out_ack;
|
goto out_ack;
|
||||||
@@ -441,9 +431,10 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
else
|
else
|
||||||
i2c->state = STATE_WRITE;
|
i2c->state = STATE_WRITE;
|
||||||
|
|
||||||
/* terminate the transfer if there is nothing to do
|
/*
|
||||||
* as this is used by the i2c probe to find devices. */
|
* Terminate the transfer if there is nothing to do
|
||||||
|
* as this is used by the i2c probe to find devices.
|
||||||
|
*/
|
||||||
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
|
if (is_lastmsg(i2c) && i2c->msg->len == 0) {
|
||||||
s3c24xx_i2c_stop(i2c, 0);
|
s3c24xx_i2c_stop(i2c, 0);
|
||||||
goto out_ack;
|
goto out_ack;
|
||||||
@@ -452,14 +443,16 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
if (i2c->state == STATE_READ)
|
if (i2c->state == STATE_READ)
|
||||||
goto prepare_read;
|
goto prepare_read;
|
||||||
|
|
||||||
/* fall through to the write state, as we will need to
|
/*
|
||||||
* send a byte as well */
|
* fall through to the write state, as we will need to
|
||||||
|
* send a byte as well
|
||||||
case STATE_WRITE:
|
|
||||||
/* we are writing data to the device... check for the
|
|
||||||
* end of the message, and if so, work out what to do
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
case STATE_WRITE:
|
||||||
|
/*
|
||||||
|
* we are writing data to the device... check for the
|
||||||
|
* end of the message, and if so, work out what to do
|
||||||
|
*/
|
||||||
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
|
if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
|
||||||
if (iicstat & S3C2410_IICSTAT_LASTBIT) {
|
if (iicstat & S3C2410_IICSTAT_LASTBIT) {
|
||||||
dev_dbg(i2c->dev, "WRITE: No Ack\n");
|
dev_dbg(i2c->dev, "WRITE: No Ack\n");
|
||||||
@@ -475,12 +468,13 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
byte = i2c->msg->buf[i2c->msg_ptr++];
|
byte = i2c->msg->buf[i2c->msg_ptr++];
|
||||||
writeb(byte, i2c->regs + S3C2410_IICDS);
|
writeb(byte, i2c->regs + S3C2410_IICDS);
|
||||||
|
|
||||||
/* delay after writing the byte to allow the
|
/*
|
||||||
|
* delay after writing the byte to allow the
|
||||||
* data setup time on the bus, as writing the
|
* data setup time on the bus, as writing the
|
||||||
* data to the register causes the first bit
|
* data to the register causes the first bit
|
||||||
* to appear on SDA, and SCL will change as
|
* to appear on SDA, and SCL will change as
|
||||||
* soon as the interrupt is acknowledged */
|
* soon as the interrupt is acknowledged
|
||||||
|
*/
|
||||||
ndelay(i2c->tx_setup);
|
ndelay(i2c->tx_setup);
|
||||||
|
|
||||||
} else if (!is_lastmsg(i2c)) {
|
} else if (!is_lastmsg(i2c)) {
|
||||||
@@ -496,10 +490,11 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
if (i2c->msg->flags & I2C_M_NOSTART) {
|
if (i2c->msg->flags & I2C_M_NOSTART) {
|
||||||
|
|
||||||
if (i2c->msg->flags & I2C_M_RD) {
|
if (i2c->msg->flags & I2C_M_RD) {
|
||||||
/* cannot do this, the controller
|
/*
|
||||||
|
* cannot do this, the controller
|
||||||
* forces us to send a new START
|
* forces us to send a new START
|
||||||
* when we change direction */
|
* when we change direction
|
||||||
|
*/
|
||||||
s3c24xx_i2c_stop(i2c, -EINVAL);
|
s3c24xx_i2c_stop(i2c, -EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,17 +507,16 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
/* send stop */
|
/* send stop */
|
||||||
|
|
||||||
s3c24xx_i2c_stop(i2c, 0);
|
s3c24xx_i2c_stop(i2c, 0);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case STATE_READ:
|
case STATE_READ:
|
||||||
/* we have a byte of data in the data register, do
|
/*
|
||||||
|
* we have a byte of data in the data register, do
|
||||||
* something with it, and then work out whether we are
|
* something with it, and then work out whether we are
|
||||||
* going to do any more read/write
|
* going to do any more read/write
|
||||||
*/
|
*/
|
||||||
|
|
||||||
byte = readb(i2c->regs + S3C2410_IICDS);
|
byte = readb(i2c->regs + S3C2410_IICDS);
|
||||||
i2c->msg->buf[i2c->msg_ptr++] = byte;
|
i2c->msg->buf[i2c->msg_ptr++] = byte;
|
||||||
|
|
||||||
@@ -537,9 +531,10 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
s3c24xx_i2c_disable_ack(i2c);
|
s3c24xx_i2c_disable_ack(i2c);
|
||||||
|
|
||||||
} else if (is_msgend(i2c)) {
|
} else if (is_msgend(i2c)) {
|
||||||
/* ok, we've read the entire buffer, see if there
|
/*
|
||||||
* is anything else we need to do */
|
* ok, we've read the entire buffer, see if there
|
||||||
|
* is anything else we need to do
|
||||||
|
*/
|
||||||
if (is_lastmsg(i2c)) {
|
if (is_lastmsg(i2c)) {
|
||||||
/* last message, send stop and complete */
|
/* last message, send stop and complete */
|
||||||
dev_dbg(i2c->dev, "READ: Send Stop\n");
|
dev_dbg(i2c->dev, "READ: Send Stop\n");
|
||||||
@@ -568,11 +563,9 @@ static int i2c_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_irq
|
/*
|
||||||
*
|
|
||||||
* top level IRQ servicing routine
|
* top level IRQ servicing routine
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
|
static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
|
||||||
{
|
{
|
||||||
struct s3c24xx_i2c *i2c = dev_id;
|
struct s3c24xx_i2c *i2c = dev_id;
|
||||||
@@ -595,9 +588,10 @@ static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id)
|
|||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* pretty much this leaves us with the fact that we've
|
/*
|
||||||
* transmitted or received whatever byte we last sent */
|
* pretty much this leaves us with the fact that we've
|
||||||
|
* transmitted or received whatever byte we last sent
|
||||||
|
*/
|
||||||
i2c_s3c_irq_nextbyte(i2c, status);
|
i2c_s3c_irq_nextbyte(i2c, status);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
@@ -630,11 +624,9 @@ static inline void s3c24xx_i2c_disable_bus(struct s3c24xx_i2c *i2c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* s3c24xx_i2c_set_master
|
/*
|
||||||
*
|
|
||||||
* get the i2c bus for a master transaction
|
* get the i2c bus for a master transaction
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
|
static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
unsigned long iicstat;
|
unsigned long iicstat;
|
||||||
@@ -652,11 +644,9 @@ static int s3c24xx_i2c_set_master(struct s3c24xx_i2c *i2c)
|
|||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_wait_idle
|
/*
|
||||||
*
|
|
||||||
* wait for the i2c bus to become idle.
|
* wait for the i2c bus to become idle.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c)
|
static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
unsigned long iicstat;
|
unsigned long iicstat;
|
||||||
@@ -706,11 +696,9 @@ static void s3c24xx_i2c_wait_idle(struct s3c24xx_i2c *i2c)
|
|||||||
dev_warn(i2c->dev, "timeout waiting for bus idle\n");
|
dev_warn(i2c->dev, "timeout waiting for bus idle\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_doxfer
|
/*
|
||||||
*
|
|
||||||
* this starts an i2c transfer
|
* this starts an i2c transfer
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
|
static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
|
||||||
struct i2c_msg *msgs, int num)
|
struct i2c_msg *msgs, int num)
|
||||||
{
|
{
|
||||||
@@ -749,9 +737,10 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
|
|||||||
|
|
||||||
ret = i2c->msg_idx;
|
ret = i2c->msg_idx;
|
||||||
|
|
||||||
/* having these next two as dev_err() makes life very
|
/*
|
||||||
* noisy when doing an i2cdetect */
|
* Having these next two as dev_err() makes life very
|
||||||
|
* noisy when doing an i2cdetect
|
||||||
|
*/
|
||||||
if (timeout == 0)
|
if (timeout == 0)
|
||||||
dev_dbg(i2c->dev, "timeout\n");
|
dev_dbg(i2c->dev, "timeout\n");
|
||||||
else if (ret != num)
|
else if (ret != num)
|
||||||
@@ -771,12 +760,10 @@ static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_xfer
|
/*
|
||||||
*
|
|
||||||
* first port of call from the i2c bus code when an message needs
|
* first port of call from the i2c bus code when an message needs
|
||||||
* transferring across the i2c bus.
|
* transferring across the i2c bus.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
static int s3c24xx_i2c_xfer(struct i2c_adapter *adap,
|
||||||
struct i2c_msg *msgs, int num)
|
struct i2c_msg *msgs, int num)
|
||||||
{
|
{
|
||||||
@@ -814,17 +801,14 @@ static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* i2c bus registration info */
|
/* i2c bus registration info */
|
||||||
|
|
||||||
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
|
static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
|
||||||
.master_xfer = s3c24xx_i2c_xfer,
|
.master_xfer = s3c24xx_i2c_xfer,
|
||||||
.functionality = s3c24xx_i2c_func,
|
.functionality = s3c24xx_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* s3c24xx_i2c_calcdivisor
|
/*
|
||||||
*
|
|
||||||
* return the divisor settings for a given frequency
|
* return the divisor settings for a given frequency
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
|
static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
|
||||||
unsigned int *div1, unsigned int *divs)
|
unsigned int *div1, unsigned int *divs)
|
||||||
{
|
{
|
||||||
@@ -850,13 +834,11 @@ static int s3c24xx_i2c_calcdivisor(unsigned long clkin, unsigned int wanted,
|
|||||||
return clkin / (calc_divs * calc_div1);
|
return clkin / (calc_divs * calc_div1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_clockrate
|
/*
|
||||||
*
|
|
||||||
* work out a divisor for the user requested frequency setting,
|
* work out a divisor for the user requested frequency setting,
|
||||||
* either by the requested frequency, or scanning the acceptable
|
* either by the requested frequency, or scanning the acceptable
|
||||||
* range of frequencies until something is found
|
* range of frequencies until something is found
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
|
static int s3c24xx_i2c_clockrate(struct s3c24xx_i2c *i2c, unsigned int *got)
|
||||||
{
|
{
|
||||||
struct s3c2410_platform_i2c *pdata = i2c->pdata;
|
struct s3c2410_platform_i2c *pdata = i2c->pdata;
|
||||||
@@ -944,7 +926,7 @@ static int s3c24xx_i2c_cpufreq_transition(struct notifier_block *nb,
|
|||||||
i2c_unlock_adapter(&i2c->adap);
|
i2c_unlock_adapter(&i2c->adap);
|
||||||
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
dev_err(i2c->dev, "cannot find frequency\n");
|
dev_err(i2c->dev, "cannot find frequency (%d)\n", ret);
|
||||||
else
|
else
|
||||||
dev_info(i2c->dev, "setting freq %d\n", got);
|
dev_info(i2c->dev, "setting freq %d\n", got);
|
||||||
}
|
}
|
||||||
@@ -995,7 +977,8 @@ static int s3c24xx_i2c_parse_dt_gpio(struct s3c24xx_i2c *i2c)
|
|||||||
|
|
||||||
ret = gpio_request(gpio, "i2c-bus");
|
ret = gpio_request(gpio, "i2c-bus");
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(i2c->dev, "gpio [%d] request failed\n", gpio);
|
dev_err(i2c->dev, "gpio [%d] request failed (%d)\n",
|
||||||
|
gpio, ret);
|
||||||
goto free_gpio;
|
goto free_gpio;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1028,11 +1011,9 @@ static void s3c24xx_i2c_dt_gpio_free(struct s3c24xx_i2c *i2c)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* s3c24xx_i2c_init
|
/*
|
||||||
*
|
|
||||||
* initialise the controller, set the IO lines and frequency
|
* initialise the controller, set the IO lines and frequency
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
|
static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
struct s3c2410_platform_i2c *pdata;
|
struct s3c2410_platform_i2c *pdata;
|
||||||
@@ -1068,11 +1049,9 @@ static int s3c24xx_i2c_init(struct s3c24xx_i2c *i2c)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
/* s3c24xx_i2c_parse_dt
|
/*
|
||||||
*
|
|
||||||
* Parse the device tree node and retreive the platform data.
|
* Parse the device tree node and retreive the platform data.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static void
|
static void
|
||||||
s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
|
s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
|
||||||
{
|
{
|
||||||
@@ -1105,17 +1084,9 @@ s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static void
|
static void
|
||||||
s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c)
|
s3c24xx_i2c_parse_dt(struct device_node *np, struct s3c24xx_i2c *i2c) { }
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* s3c24xx_i2c_probe
|
|
||||||
*
|
|
||||||
* called by the bus driver when a suitable device is found
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct s3c24xx_i2c *i2c;
|
struct s3c24xx_i2c *i2c;
|
||||||
@@ -1156,7 +1127,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||||||
init_waitqueue_head(&i2c->wait);
|
init_waitqueue_head(&i2c->wait);
|
||||||
|
|
||||||
/* find the clock and enable it */
|
/* find the clock and enable it */
|
||||||
|
|
||||||
i2c->dev = &pdev->dev;
|
i2c->dev = &pdev->dev;
|
||||||
i2c->clk = devm_clk_get(&pdev->dev, "i2c");
|
i2c->clk = devm_clk_get(&pdev->dev, "i2c");
|
||||||
if (IS_ERR(i2c->clk)) {
|
if (IS_ERR(i2c->clk)) {
|
||||||
@@ -1166,9 +1136,7 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
|
dev_dbg(&pdev->dev, "clock source %p\n", i2c->clk);
|
||||||
|
|
||||||
|
|
||||||
/* map the registers */
|
/* map the registers */
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
i2c->regs = devm_ioremap_resource(&pdev->dev, res);
|
i2c->regs = devm_ioremap_resource(&pdev->dev, res);
|
||||||
|
|
||||||
@@ -1179,33 +1147,35 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||||||
i2c->regs, res);
|
i2c->regs, res);
|
||||||
|
|
||||||
/* setup info block for the i2c core */
|
/* setup info block for the i2c core */
|
||||||
|
|
||||||
i2c->adap.algo_data = i2c;
|
i2c->adap.algo_data = i2c;
|
||||||
i2c->adap.dev.parent = &pdev->dev;
|
i2c->adap.dev.parent = &pdev->dev;
|
||||||
|
|
||||||
i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
|
i2c->pctrl = devm_pinctrl_get_select_default(i2c->dev);
|
||||||
|
|
||||||
/* inititalise the i2c gpio lines */
|
/* inititalise the i2c gpio lines */
|
||||||
|
if (i2c->pdata->cfg_gpio)
|
||||||
if (i2c->pdata->cfg_gpio) {
|
|
||||||
i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));
|
i2c->pdata->cfg_gpio(to_platform_device(i2c->dev));
|
||||||
} else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c)) {
|
else if (IS_ERR(i2c->pctrl) && s3c24xx_i2c_parse_dt_gpio(i2c))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
|
||||||
|
|
||||||
/* initialise the i2c controller */
|
/* initialise the i2c controller */
|
||||||
|
ret = clk_prepare_enable(i2c->clk);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(&pdev->dev, "I2C clock enable failed\n");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
clk_prepare_enable(i2c->clk);
|
|
||||||
ret = s3c24xx_i2c_init(i2c);
|
ret = s3c24xx_i2c_init(i2c);
|
||||||
clk_disable(i2c->clk);
|
clk_disable(i2c->clk);
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(&pdev->dev, "I2C controller init failed\n");
|
dev_err(&pdev->dev, "I2C controller init failed\n");
|
||||||
|
clk_unprepare(i2c->clk);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
/* find the IRQ for this unit (note, this relies on the init call to
|
|
||||||
|
/*
|
||||||
|
* find the IRQ for this unit (note, this relies on the init call to
|
||||||
* ensure no current IRQs pending
|
* ensure no current IRQs pending
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (!(i2c->quirks & QUIRK_POLL)) {
|
if (!(i2c->quirks & QUIRK_POLL)) {
|
||||||
i2c->irq = ret = platform_get_irq(pdev, 0);
|
i2c->irq = ret = platform_get_irq(pdev, 0);
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
@@ -1214,9 +1184,8 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq, 0,
|
ret = devm_request_irq(&pdev->dev, i2c->irq, s3c24xx_i2c_irq,
|
||||||
dev_name(&pdev->dev), i2c);
|
0, dev_name(&pdev->dev), i2c);
|
||||||
|
|
||||||
if (ret != 0) {
|
if (ret != 0) {
|
||||||
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
|
dev_err(&pdev->dev, "cannot claim IRQ %d\n", i2c->irq);
|
||||||
clk_unprepare(i2c->clk);
|
clk_unprepare(i2c->clk);
|
||||||
@@ -1231,12 +1200,12 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Note, previous versions of the driver used i2c_add_adapter()
|
/*
|
||||||
|
* Note, previous versions of the driver used i2c_add_adapter()
|
||||||
* to add the bus at any number. We now pass the bus number via
|
* to add the bus at any number. We now pass the bus number via
|
||||||
* the platform data, so if unset it will now default to always
|
* the platform data, so if unset it will now default to always
|
||||||
* being bus 0.
|
* being bus 0.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
i2c->adap.nr = i2c->pdata->bus_num;
|
i2c->adap.nr = i2c->pdata->bus_num;
|
||||||
i2c->adap.dev.of_node = pdev->dev.of_node;
|
i2c->adap.dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
@@ -1257,11 +1226,6 @@ static int s3c24xx_i2c_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* s3c24xx_i2c_remove
|
|
||||||
*
|
|
||||||
* called when device is removed from the bus
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int s3c24xx_i2c_remove(struct platform_device *pdev)
|
static int s3c24xx_i2c_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
|
struct s3c24xx_i2c *i2c = platform_get_drvdata(pdev);
|
||||||
@@ -1316,14 +1280,8 @@ static int s3c24xx_i2c_resume_noirq(struct device *dev)
|
|||||||
|
|
||||||
#ifdef CONFIG_PM
|
#ifdef CONFIG_PM
|
||||||
static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
|
static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
|
||||||
#ifdef CONFIG_PM_SLEEP
|
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(s3c24xx_i2c_suspend_noirq,
|
||||||
.suspend_noirq = s3c24xx_i2c_suspend_noirq,
|
s3c24xx_i2c_resume_noirq)
|
||||||
.resume_noirq = s3c24xx_i2c_resume_noirq,
|
|
||||||
.freeze_noirq = s3c24xx_i2c_suspend_noirq,
|
|
||||||
.thaw_noirq = s3c24xx_i2c_resume_noirq,
|
|
||||||
.poweroff_noirq = s3c24xx_i2c_suspend_noirq,
|
|
||||||
.restore_noirq = s3c24xx_i2c_resume_noirq,
|
|
||||||
#endif
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops)
|
#define S3C24XX_DEV_PM_OPS (&s3c24xx_i2c_dev_pm_ops)
|
||||||
@@ -1331,8 +1289,6 @@ static const struct dev_pm_ops s3c24xx_i2c_dev_pm_ops = {
|
|||||||
#define S3C24XX_DEV_PM_OPS NULL
|
#define S3C24XX_DEV_PM_OPS NULL
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* device driver for platform bus bits */
|
|
||||||
|
|
||||||
static struct platform_driver s3c24xx_i2c_driver = {
|
static struct platform_driver s3c24xx_i2c_driver = {
|
||||||
.probe = s3c24xx_i2c_probe,
|
.probe = s3c24xx_i2c_probe,
|
||||||
.remove = s3c24xx_i2c_remove,
|
.remove = s3c24xx_i2c_remove,
|
||||||
|
@@ -398,8 +398,7 @@ static void sh_mobile_i2c_get_data(struct sh_mobile_i2c_data *pd,
|
|||||||
{
|
{
|
||||||
switch (pd->pos) {
|
switch (pd->pos) {
|
||||||
case -1:
|
case -1:
|
||||||
*buf = (pd->msg->addr & 0x7f) << 1;
|
*buf = i2c_8bit_addr_from_msg(pd->msg);
|
||||||
*buf |= (pd->msg->flags & I2C_M_RD) ? 1 : 0;
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
*buf = pd->msg->buf[pd->pos];
|
*buf = pd->msg->buf[pd->pos];
|
||||||
|
@@ -190,9 +190,7 @@ static void i2c_sirfsoc_set_address(struct sirfsoc_i2c *siic,
|
|||||||
|
|
||||||
writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
|
writel(regval, siic->base + SIRFSOC_I2C_CMD(siic->cmd_ptr++));
|
||||||
|
|
||||||
addr = msg->addr << 1; /* Generate address */
|
addr = i2c_8bit_addr_from_msg(msg);
|
||||||
if (msg->flags & I2C_M_RD)
|
|
||||||
addr |= 1;
|
|
||||||
|
|
||||||
/* Reverse direction bit */
|
/* Reverse direction bit */
|
||||||
if (msg->flags & I2C_M_REV_DIR_ADDR)
|
if (msg->flags & I2C_M_REV_DIR_ADDR)
|
||||||
|
@@ -337,10 +337,42 @@ static void st_i2c_hw_config(struct st_i2c_dev *i2c_dev)
|
|||||||
writel_relaxed(val, i2c_dev->base + SSC_NOISE_SUPP_WIDTH_DATAOUT);
|
writel_relaxed(val, i2c_dev->base + SSC_NOISE_SUPP_WIDTH_DATAOUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int st_i2c_recover_bus(struct i2c_adapter *i2c_adap)
|
||||||
|
{
|
||||||
|
struct st_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
|
||||||
|
u32 ctl;
|
||||||
|
|
||||||
|
dev_dbg(i2c_dev->dev, "Trying to recover bus\n");
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SSP IP is dual role SPI/I2C to generate 9 clock pulses
|
||||||
|
* we switch to SPI node, 9 bit words and write a 0. This
|
||||||
|
* has been validate with a oscilloscope and is easier
|
||||||
|
* than switching to GPIO mode.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Disable interrupts */
|
||||||
|
writel_relaxed(0, i2c_dev->base + SSC_IEN);
|
||||||
|
|
||||||
|
st_i2c_hw_config(i2c_dev);
|
||||||
|
|
||||||
|
ctl = SSC_CTL_EN | SSC_CTL_MS | SSC_CTL_EN_RX_FIFO | SSC_CTL_EN_TX_FIFO;
|
||||||
|
st_i2c_set_bits(i2c_dev->base + SSC_CTL, ctl);
|
||||||
|
|
||||||
|
st_i2c_clr_bits(i2c_dev->base + SSC_I2C, SSC_I2C_I2CM);
|
||||||
|
usleep_range(8000, 10000);
|
||||||
|
|
||||||
|
writel_relaxed(0, i2c_dev->base + SSC_TBUF);
|
||||||
|
usleep_range(2000, 4000);
|
||||||
|
st_i2c_set_bits(i2c_dev->base + SSC_I2C, SSC_I2C_I2CM);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev)
|
static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev)
|
||||||
{
|
{
|
||||||
u32 sta;
|
u32 sta;
|
||||||
int i;
|
int i, ret;
|
||||||
|
|
||||||
for (i = 0; i < 10; i++) {
|
for (i = 0; i < 10; i++) {
|
||||||
sta = readl_relaxed(i2c_dev->base + SSC_STA);
|
sta = readl_relaxed(i2c_dev->base + SSC_STA);
|
||||||
@@ -352,6 +384,12 @@ static int st_i2c_wait_free_bus(struct st_i2c_dev *i2c_dev)
|
|||||||
|
|
||||||
dev_err(i2c_dev->dev, "bus not free (status = 0x%08x)\n", sta);
|
dev_err(i2c_dev->dev, "bus not free (status = 0x%08x)\n", sta);
|
||||||
|
|
||||||
|
ret = i2c_recover_bus(&i2c_dev->adap);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -614,8 +652,7 @@ static int st_i2c_xfer_msg(struct st_i2c_dev *i2c_dev, struct i2c_msg *msg,
|
|||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
c->addr = (u8)(msg->addr << 1);
|
c->addr = i2c_8bit_addr_from_msg(msg);
|
||||||
c->addr |= (msg->flags & I2C_M_RD);
|
|
||||||
c->buf = msg->buf;
|
c->buf = msg->buf;
|
||||||
c->count = msg->len;
|
c->count = msg->len;
|
||||||
c->xfered = 0;
|
c->xfered = 0;
|
||||||
@@ -744,6 +781,10 @@ static struct i2c_algorithm st_i2c_algo = {
|
|||||||
.functionality = st_i2c_func,
|
.functionality = st_i2c_func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct i2c_bus_recovery_info st_i2c_recovery_info = {
|
||||||
|
.recover_bus = st_i2c_recover_bus,
|
||||||
|
};
|
||||||
|
|
||||||
static int st_i2c_of_get_deglitch(struct device_node *np,
|
static int st_i2c_of_get_deglitch(struct device_node *np,
|
||||||
struct st_i2c_dev *i2c_dev)
|
struct st_i2c_dev *i2c_dev)
|
||||||
{
|
{
|
||||||
@@ -826,6 +867,7 @@ static int st_i2c_probe(struct platform_device *pdev)
|
|||||||
adap->timeout = 2 * HZ;
|
adap->timeout = 2 * HZ;
|
||||||
adap->retries = 0;
|
adap->retries = 0;
|
||||||
adap->algo = &st_i2c_algo;
|
adap->algo = &st_i2c_algo;
|
||||||
|
adap->bus_recovery_info = &st_i2c_recovery_info;
|
||||||
adap->dev.parent = &pdev->dev;
|
adap->dev.parent = &pdev->dev;
|
||||||
adap->dev.of_node = pdev->dev.of_node;
|
adap->dev.of_node = pdev->dev.of_node;
|
||||||
|
|
||||||
|
@@ -38,6 +38,7 @@
|
|||||||
#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
|
#define I2C_CNFG_DEBOUNCE_CNT_SHIFT 12
|
||||||
#define I2C_CNFG_PACKET_MODE_EN (1<<10)
|
#define I2C_CNFG_PACKET_MODE_EN (1<<10)
|
||||||
#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
|
#define I2C_CNFG_NEW_MASTER_FSM (1<<11)
|
||||||
|
#define I2C_CNFG_MULTI_MASTER_MODE (1<<17)
|
||||||
#define I2C_STATUS 0x01C
|
#define I2C_STATUS 0x01C
|
||||||
#define I2C_SL_CNFG 0x020
|
#define I2C_SL_CNFG 0x020
|
||||||
#define I2C_SL_CNFG_NACK (1<<1)
|
#define I2C_SL_CNFG_NACK (1<<1)
|
||||||
@@ -106,6 +107,9 @@
|
|||||||
#define I2C_SLV_CONFIG_LOAD (1 << 1)
|
#define I2C_SLV_CONFIG_LOAD (1 << 1)
|
||||||
#define I2C_TIMEOUT_CONFIG_LOAD (1 << 2)
|
#define I2C_TIMEOUT_CONFIG_LOAD (1 << 2)
|
||||||
|
|
||||||
|
#define I2C_CLKEN_OVERRIDE 0x090
|
||||||
|
#define I2C_MST_CORE_CLKEN_OVR (1 << 0)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* msg_end_type: The bus control which need to be send at end of transfer.
|
* msg_end_type: The bus control which need to be send at end of transfer.
|
||||||
* @MSG_END_STOP: Send stop pulse at end of transfer.
|
* @MSG_END_STOP: Send stop pulse at end of transfer.
|
||||||
@@ -143,6 +147,8 @@ struct tegra_i2c_hw_feature {
|
|||||||
int clk_divisor_hs_mode;
|
int clk_divisor_hs_mode;
|
||||||
int clk_divisor_std_fast_mode;
|
int clk_divisor_std_fast_mode;
|
||||||
u16 clk_divisor_fast_plus_mode;
|
u16 clk_divisor_fast_plus_mode;
|
||||||
|
bool has_multi_master_mode;
|
||||||
|
bool has_slcg_override_reg;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -184,6 +190,7 @@ struct tegra_i2c_dev {
|
|||||||
u32 bus_clk_rate;
|
u32 bus_clk_rate;
|
||||||
u16 clk_divisor_non_hs_mode;
|
u16 clk_divisor_non_hs_mode;
|
||||||
bool is_suspended;
|
bool is_suspended;
|
||||||
|
bool is_multimaster_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg)
|
static void dvc_writel(struct tegra_i2c_dev *i2c_dev, u32 val, unsigned long reg)
|
||||||
@@ -438,6 +445,10 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||||||
|
|
||||||
val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
|
val = I2C_CNFG_NEW_MASTER_FSM | I2C_CNFG_PACKET_MODE_EN |
|
||||||
(0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
|
(0x2 << I2C_CNFG_DEBOUNCE_CNT_SHIFT);
|
||||||
|
|
||||||
|
if (i2c_dev->hw->has_multi_master_mode)
|
||||||
|
val |= I2C_CNFG_MULTI_MASTER_MODE;
|
||||||
|
|
||||||
i2c_writel(i2c_dev, val, I2C_CNFG);
|
i2c_writel(i2c_dev, val, I2C_CNFG);
|
||||||
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
|
i2c_writel(i2c_dev, 0, I2C_INT_MASK);
|
||||||
|
|
||||||
@@ -463,25 +474,29 @@ static int tegra_i2c_init(struct tegra_i2c_dev *i2c_dev)
|
|||||||
if (tegra_i2c_flush_fifos(i2c_dev))
|
if (tegra_i2c_flush_fifos(i2c_dev))
|
||||||
err = -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
|
|
||||||
|
if (i2c_dev->is_multimaster_mode && i2c_dev->hw->has_slcg_override_reg)
|
||||||
|
i2c_writel(i2c_dev, I2C_MST_CORE_CLKEN_OVR, I2C_CLKEN_OVERRIDE);
|
||||||
|
|
||||||
if (i2c_dev->hw->has_config_load_reg) {
|
if (i2c_dev->hw->has_config_load_reg) {
|
||||||
i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
|
i2c_writel(i2c_dev, I2C_MSTR_CONFIG_LOAD, I2C_CONFIG_LOAD);
|
||||||
while (i2c_readl(i2c_dev, I2C_CONFIG_LOAD) != 0) {
|
while (i2c_readl(i2c_dev, I2C_CONFIG_LOAD) != 0) {
|
||||||
if (time_after(jiffies, timeout)) {
|
if (time_after(jiffies, timeout)) {
|
||||||
dev_warn(i2c_dev->dev,
|
dev_warn(i2c_dev->dev,
|
||||||
"timeout waiting for config load\n");
|
"timeout waiting for config load\n");
|
||||||
return -ETIMEDOUT;
|
err = -ETIMEDOUT;
|
||||||
|
goto err;
|
||||||
}
|
}
|
||||||
msleep(1);
|
msleep(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tegra_i2c_clock_disable(i2c_dev);
|
|
||||||
|
|
||||||
if (i2c_dev->irq_disabled) {
|
if (i2c_dev->irq_disabled) {
|
||||||
i2c_dev->irq_disabled = 0;
|
i2c_dev->irq_disabled = 0;
|
||||||
enable_irq(i2c_dev->irq);
|
enable_irq(i2c_dev->irq);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
tegra_i2c_clock_disable(i2c_dev);
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -688,6 +703,20 @@ static u32 tegra_i2c_func(struct i2c_adapter *adap)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void tegra_i2c_parse_dt(struct tegra_i2c_dev *i2c_dev)
|
||||||
|
{
|
||||||
|
struct device_node *np = i2c_dev->dev->of_node;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = of_property_read_u32(np, "clock-frequency",
|
||||||
|
&i2c_dev->bus_clk_rate);
|
||||||
|
if (ret)
|
||||||
|
i2c_dev->bus_clk_rate = 100000; /* default clock rate */
|
||||||
|
|
||||||
|
i2c_dev->is_multimaster_mode = of_property_read_bool(np,
|
||||||
|
"multi-master");
|
||||||
|
}
|
||||||
|
|
||||||
static const struct i2c_algorithm tegra_i2c_algo = {
|
static const struct i2c_algorithm tegra_i2c_algo = {
|
||||||
.master_xfer = tegra_i2c_xfer,
|
.master_xfer = tegra_i2c_xfer,
|
||||||
.functionality = tegra_i2c_func,
|
.functionality = tegra_i2c_func,
|
||||||
@@ -707,6 +736,8 @@ static const struct tegra_i2c_hw_feature tegra20_i2c_hw = {
|
|||||||
.clk_divisor_std_fast_mode = 0,
|
.clk_divisor_std_fast_mode = 0,
|
||||||
.clk_divisor_fast_plus_mode = 0,
|
.clk_divisor_fast_plus_mode = 0,
|
||||||
.has_config_load_reg = false,
|
.has_config_load_reg = false,
|
||||||
|
.has_multi_master_mode = false,
|
||||||
|
.has_slcg_override_reg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
||||||
@@ -717,6 +748,8 @@ static const struct tegra_i2c_hw_feature tegra30_i2c_hw = {
|
|||||||
.clk_divisor_std_fast_mode = 0,
|
.clk_divisor_std_fast_mode = 0,
|
||||||
.clk_divisor_fast_plus_mode = 0,
|
.clk_divisor_fast_plus_mode = 0,
|
||||||
.has_config_load_reg = false,
|
.has_config_load_reg = false,
|
||||||
|
.has_multi_master_mode = false,
|
||||||
|
.has_slcg_override_reg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
||||||
@@ -727,6 +760,8 @@ static const struct tegra_i2c_hw_feature tegra114_i2c_hw = {
|
|||||||
.clk_divisor_std_fast_mode = 0x19,
|
.clk_divisor_std_fast_mode = 0x19,
|
||||||
.clk_divisor_fast_plus_mode = 0x10,
|
.clk_divisor_fast_plus_mode = 0x10,
|
||||||
.has_config_load_reg = false,
|
.has_config_load_reg = false,
|
||||||
|
.has_multi_master_mode = false,
|
||||||
|
.has_slcg_override_reg = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
|
static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
|
||||||
@@ -737,10 +772,25 @@ static const struct tegra_i2c_hw_feature tegra124_i2c_hw = {
|
|||||||
.clk_divisor_std_fast_mode = 0x19,
|
.clk_divisor_std_fast_mode = 0x19,
|
||||||
.clk_divisor_fast_plus_mode = 0x10,
|
.clk_divisor_fast_plus_mode = 0x10,
|
||||||
.has_config_load_reg = true,
|
.has_config_load_reg = true,
|
||||||
|
.has_multi_master_mode = false,
|
||||||
|
.has_slcg_override_reg = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct tegra_i2c_hw_feature tegra210_i2c_hw = {
|
||||||
|
.has_continue_xfer_support = true,
|
||||||
|
.has_per_pkt_xfer_complete_irq = true,
|
||||||
|
.has_single_clk_source = true,
|
||||||
|
.clk_divisor_hs_mode = 1,
|
||||||
|
.clk_divisor_std_fast_mode = 0x19,
|
||||||
|
.clk_divisor_fast_plus_mode = 0x10,
|
||||||
|
.has_config_load_reg = true,
|
||||||
|
.has_multi_master_mode = true,
|
||||||
|
.has_slcg_override_reg = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Match table for of_platform binding */
|
/* Match table for of_platform binding */
|
||||||
static const struct of_device_id tegra_i2c_of_match[] = {
|
static const struct of_device_id tegra_i2c_of_match[] = {
|
||||||
|
{ .compatible = "nvidia,tegra210-i2c", .data = &tegra210_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
|
{ .compatible = "nvidia,tegra124-i2c", .data = &tegra124_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
|
{ .compatible = "nvidia,tegra114-i2c", .data = &tegra114_i2c_hw, },
|
||||||
{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
|
{ .compatible = "nvidia,tegra30-i2c", .data = &tegra30_i2c_hw, },
|
||||||
@@ -797,10 +847,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(i2c_dev->rst);
|
return PTR_ERR(i2c_dev->rst);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = of_property_read_u32(i2c_dev->dev->of_node, "clock-frequency",
|
tegra_i2c_parse_dt(i2c_dev);
|
||||||
&i2c_dev->bus_clk_rate);
|
|
||||||
if (ret)
|
|
||||||
i2c_dev->bus_clk_rate = 100000; /* default clock rate */
|
|
||||||
|
|
||||||
i2c_dev->hw = &tegra20_i2c_hw;
|
i2c_dev->hw = &tegra20_i2c_hw;
|
||||||
|
|
||||||
@@ -853,6 +900,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||||||
goto unprepare_fast_clk;
|
goto unprepare_fast_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (i2c_dev->is_multimaster_mode) {
|
||||||
|
ret = clk_enable(i2c_dev->div_clk);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(i2c_dev->dev, "div_clk enable failed %d\n",
|
||||||
|
ret);
|
||||||
|
goto unprepare_div_clk;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ret = tegra_i2c_init(i2c_dev);
|
ret = tegra_i2c_init(i2c_dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to initialize i2c controller");
|
dev_err(&pdev->dev, "Failed to initialize i2c controller");
|
||||||
@@ -863,7 +919,7 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||||||
tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
|
tegra_i2c_isr, 0, dev_name(&pdev->dev), i2c_dev);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
|
dev_err(&pdev->dev, "Failed to request irq %i\n", i2c_dev->irq);
|
||||||
goto unprepare_div_clk;
|
goto disable_div_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
|
i2c_set_adapdata(&i2c_dev->adapter, i2c_dev);
|
||||||
@@ -878,11 +934,15 @@ static int tegra_i2c_probe(struct platform_device *pdev)
|
|||||||
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
|
ret = i2c_add_numbered_adapter(&i2c_dev->adapter);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to add I2C adapter\n");
|
dev_err(&pdev->dev, "Failed to add I2C adapter\n");
|
||||||
goto unprepare_div_clk;
|
goto disable_div_clk;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
disable_div_clk:
|
||||||
|
if (i2c_dev->is_multimaster_mode)
|
||||||
|
clk_disable(i2c_dev->div_clk);
|
||||||
|
|
||||||
unprepare_div_clk:
|
unprepare_div_clk:
|
||||||
clk_unprepare(i2c_dev->div_clk);
|
clk_unprepare(i2c_dev->div_clk);
|
||||||
|
|
||||||
@@ -898,6 +958,9 @@ static int tegra_i2c_remove(struct platform_device *pdev)
|
|||||||
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
struct tegra_i2c_dev *i2c_dev = platform_get_drvdata(pdev);
|
||||||
i2c_del_adapter(&i2c_dev->adapter);
|
i2c_del_adapter(&i2c_dev->adapter);
|
||||||
|
|
||||||
|
if (i2c_dev->is_multimaster_mode)
|
||||||
|
clk_disable(i2c_dev->div_clk);
|
||||||
|
|
||||||
clk_unprepare(i2c_dev->div_clk);
|
clk_unprepare(i2c_dev->div_clk);
|
||||||
if (!i2c_dev->hw->has_single_clk_source)
|
if (!i2c_dev->hw->has_single_clk_source)
|
||||||
clk_unprepare(i2c_dev->fast_clk);
|
clk_unprepare(i2c_dev->fast_clk);
|
||||||
|
@@ -524,7 +524,7 @@ static int uniphier_fi2c_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0) {
|
if (irq < 0) {
|
||||||
dev_err(dev, "failed to get IRQ number");
|
dev_err(dev, "failed to get IRQ number\n");
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -381,7 +381,7 @@ static int uniphier_i2c_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0) {
|
if (irq < 0) {
|
||||||
dev_err(dev, "failed to get IRQ number");
|
dev_err(dev, "failed to get IRQ number\n");
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -954,48 +954,40 @@ static int i2c_check_addr_busy(struct i2c_adapter *adapter, int addr)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i2c_lock_adapter - Get exclusive access to an I2C bus segment
|
* i2c_adapter_lock_bus - Get exclusive access to an I2C bus segment
|
||||||
* @adapter: Target I2C bus segment
|
* @adapter: Target I2C bus segment
|
||||||
|
* @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT
|
||||||
|
* locks only this branch in the adapter tree
|
||||||
*/
|
*/
|
||||||
void i2c_lock_adapter(struct i2c_adapter *adapter)
|
static void i2c_adapter_lock_bus(struct i2c_adapter *adapter,
|
||||||
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
|
rt_mutex_lock(&adapter->bus_lock);
|
||||||
|
|
||||||
if (parent)
|
|
||||||
i2c_lock_adapter(parent);
|
|
||||||
else
|
|
||||||
rt_mutex_lock(&adapter->bus_lock);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL_GPL(i2c_lock_adapter);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* i2c_trylock_adapter - Try to get exclusive access to an I2C bus segment
|
|
||||||
* @adapter: Target I2C bus segment
|
|
||||||
*/
|
|
||||||
static int i2c_trylock_adapter(struct i2c_adapter *adapter)
|
|
||||||
{
|
|
||||||
struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
|
|
||||||
|
|
||||||
if (parent)
|
|
||||||
return i2c_trylock_adapter(parent);
|
|
||||||
else
|
|
||||||
return rt_mutex_trylock(&adapter->bus_lock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* i2c_unlock_adapter - Release exclusive access to an I2C bus segment
|
* i2c_adapter_trylock_bus - Try to get exclusive access to an I2C bus segment
|
||||||
* @adapter: Target I2C bus segment
|
* @adapter: Target I2C bus segment
|
||||||
|
* @flags: I2C_LOCK_ROOT_ADAPTER trylocks the root i2c adapter, I2C_LOCK_SEGMENT
|
||||||
|
* trylocks only this branch in the adapter tree
|
||||||
*/
|
*/
|
||||||
void i2c_unlock_adapter(struct i2c_adapter *adapter)
|
static int i2c_adapter_trylock_bus(struct i2c_adapter *adapter,
|
||||||
|
unsigned int flags)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter);
|
return rt_mutex_trylock(&adapter->bus_lock);
|
||||||
|
}
|
||||||
if (parent)
|
|
||||||
i2c_unlock_adapter(parent);
|
/**
|
||||||
else
|
* i2c_adapter_unlock_bus - Release exclusive access to an I2C bus segment
|
||||||
rt_mutex_unlock(&adapter->bus_lock);
|
* @adapter: Target I2C bus segment
|
||||||
|
* @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT
|
||||||
|
* unlocks only this branch in the adapter tree
|
||||||
|
*/
|
||||||
|
static void i2c_adapter_unlock_bus(struct i2c_adapter *adapter,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
rt_mutex_unlock(&adapter->bus_lock);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_unlock_adapter);
|
|
||||||
|
|
||||||
static void i2c_dev_set_name(struct i2c_adapter *adap,
|
static void i2c_dev_set_name(struct i2c_adapter *adap,
|
||||||
struct i2c_client *client)
|
struct i2c_client *client)
|
||||||
@@ -1541,7 +1533,14 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!adap->lock_bus) {
|
||||||
|
adap->lock_bus = i2c_adapter_lock_bus;
|
||||||
|
adap->trylock_bus = i2c_adapter_trylock_bus;
|
||||||
|
adap->unlock_bus = i2c_adapter_unlock_bus;
|
||||||
|
}
|
||||||
|
|
||||||
rt_mutex_init(&adap->bus_lock);
|
rt_mutex_init(&adap->bus_lock);
|
||||||
|
rt_mutex_init(&adap->mux_lock);
|
||||||
mutex_init(&adap->userspace_clients_lock);
|
mutex_init(&adap->userspace_clients_lock);
|
||||||
INIT_LIST_HEAD(&adap->userspace_clients);
|
INIT_LIST_HEAD(&adap->userspace_clients);
|
||||||
|
|
||||||
@@ -1559,6 +1558,7 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||||||
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
|
||||||
|
|
||||||
pm_runtime_no_callbacks(&adap->dev);
|
pm_runtime_no_callbacks(&adap->dev);
|
||||||
|
pm_suspend_ignore_children(&adap->dev, true);
|
||||||
pm_runtime_enable(&adap->dev);
|
pm_runtime_enable(&adap->dev);
|
||||||
|
|
||||||
#ifdef CONFIG_I2C_COMPAT
|
#ifdef CONFIG_I2C_COMPAT
|
||||||
@@ -1594,10 +1594,12 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
|
|||||||
|
|
||||||
bri->get_scl = get_scl_gpio_value;
|
bri->get_scl = get_scl_gpio_value;
|
||||||
bri->set_scl = set_scl_gpio_value;
|
bri->set_scl = set_scl_gpio_value;
|
||||||
} else if (!bri->set_scl || !bri->get_scl) {
|
} else if (bri->recover_bus == i2c_generic_scl_recovery) {
|
||||||
/* Generic SCL recovery */
|
/* Generic SCL recovery */
|
||||||
dev_err(&adap->dev, "No {get|set}_gpio() found, not using recovery\n");
|
if (!bri->set_scl || !bri->get_scl) {
|
||||||
adap->bus_recovery_info = NULL;
|
dev_err(&adap->dev, "No {get|set}_scl() found, not using recovery\n");
|
||||||
|
adap->bus_recovery_info = NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2309,16 +2311,16 @@ int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (in_atomic() || irqs_disabled()) {
|
if (in_atomic() || irqs_disabled()) {
|
||||||
ret = i2c_trylock_adapter(adap);
|
ret = adap->trylock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
if (!ret)
|
if (!ret)
|
||||||
/* I2C activity is ongoing. */
|
/* I2C activity is ongoing. */
|
||||||
return -EAGAIN;
|
return -EAGAIN;
|
||||||
} else {
|
} else {
|
||||||
i2c_lock_adapter(adap);
|
i2c_lock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = __i2c_transfer(adap, msgs, num);
|
ret = __i2c_transfer(adap, msgs, num);
|
||||||
i2c_unlock_adapter(adap);
|
i2c_unlock_bus(adap, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
} else {
|
} else {
|
||||||
@@ -2646,7 +2648,7 @@ static u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count)
|
|||||||
static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
|
static u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg)
|
||||||
{
|
{
|
||||||
/* The address will be sent first */
|
/* The address will be sent first */
|
||||||
u8 addr = (msg->addr << 1) | !!(msg->flags & I2C_M_RD);
|
u8 addr = i2c_8bit_addr_from_msg(msg);
|
||||||
pec = i2c_smbus_pec(pec, &addr, 1);
|
pec = i2c_smbus_pec(pec, &addr, 1);
|
||||||
|
|
||||||
/* The data buffer follows */
|
/* The data buffer follows */
|
||||||
@@ -3093,7 +3095,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
|
|||||||
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
|
flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB;
|
||||||
|
|
||||||
if (adapter->algo->smbus_xfer) {
|
if (adapter->algo->smbus_xfer) {
|
||||||
i2c_lock_adapter(adapter);
|
i2c_lock_bus(adapter, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
/* Retry automatically on arbitration loss */
|
/* Retry automatically on arbitration loss */
|
||||||
orig_jiffies = jiffies;
|
orig_jiffies = jiffies;
|
||||||
@@ -3107,7 +3109,7 @@ s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags,
|
|||||||
orig_jiffies + adapter->timeout))
|
orig_jiffies + adapter->timeout))
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i2c_unlock_adapter(adapter);
|
i2c_unlock_bus(adapter, I2C_LOCK_SEGMENT);
|
||||||
|
|
||||||
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
|
if (res != -EOPNOTSUPP || !adapter->algo->master_xfer)
|
||||||
goto trace;
|
goto trace;
|
||||||
|
@@ -31,30 +31,66 @@
|
|||||||
struct i2c_mux_priv {
|
struct i2c_mux_priv {
|
||||||
struct i2c_adapter adap;
|
struct i2c_adapter adap;
|
||||||
struct i2c_algorithm algo;
|
struct i2c_algorithm algo;
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct i2c_adapter *parent;
|
|
||||||
struct device *mux_dev;
|
|
||||||
void *mux_priv;
|
|
||||||
u32 chan_id;
|
u32 chan_id;
|
||||||
|
|
||||||
int (*select)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
|
|
||||||
int (*deselect)(struct i2c_adapter *, void *mux_priv, u32 chan_id);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int __i2c_mux_master_xfer(struct i2c_adapter *adap,
|
||||||
|
struct i2c_msg msgs[], int num)
|
||||||
|
{
|
||||||
|
struct i2c_mux_priv *priv = adap->algo_data;
|
||||||
|
struct i2c_mux_core *muxc = priv->muxc;
|
||||||
|
struct i2c_adapter *parent = muxc->parent;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Switch to the right mux port and perform the transfer. */
|
||||||
|
|
||||||
|
ret = muxc->select(muxc, priv->chan_id);
|
||||||
|
if (ret >= 0)
|
||||||
|
ret = __i2c_transfer(parent, msgs, num);
|
||||||
|
if (muxc->deselect)
|
||||||
|
muxc->deselect(muxc, priv->chan_id);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static int i2c_mux_master_xfer(struct i2c_adapter *adap,
|
static int i2c_mux_master_xfer(struct i2c_adapter *adap,
|
||||||
struct i2c_msg msgs[], int num)
|
struct i2c_msg msgs[], int num)
|
||||||
{
|
{
|
||||||
struct i2c_mux_priv *priv = adap->algo_data;
|
struct i2c_mux_priv *priv = adap->algo_data;
|
||||||
struct i2c_adapter *parent = priv->parent;
|
struct i2c_mux_core *muxc = priv->muxc;
|
||||||
|
struct i2c_adapter *parent = muxc->parent;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Switch to the right mux port and perform the transfer. */
|
/* Switch to the right mux port and perform the transfer. */
|
||||||
|
|
||||||
ret = priv->select(parent, priv->mux_priv, priv->chan_id);
|
ret = muxc->select(muxc, priv->chan_id);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
ret = __i2c_transfer(parent, msgs, num);
|
ret = i2c_transfer(parent, msgs, num);
|
||||||
if (priv->deselect)
|
if (muxc->deselect)
|
||||||
priv->deselect(parent, priv->mux_priv, priv->chan_id);
|
muxc->deselect(muxc, priv->chan_id);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __i2c_mux_smbus_xfer(struct i2c_adapter *adap,
|
||||||
|
u16 addr, unsigned short flags,
|
||||||
|
char read_write, u8 command,
|
||||||
|
int size, union i2c_smbus_data *data)
|
||||||
|
{
|
||||||
|
struct i2c_mux_priv *priv = adap->algo_data;
|
||||||
|
struct i2c_mux_core *muxc = priv->muxc;
|
||||||
|
struct i2c_adapter *parent = muxc->parent;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Select the right mux port and perform the transfer. */
|
||||||
|
|
||||||
|
ret = muxc->select(muxc, priv->chan_id);
|
||||||
|
if (ret >= 0)
|
||||||
|
ret = parent->algo->smbus_xfer(parent, addr, flags,
|
||||||
|
read_write, command, size, data);
|
||||||
|
if (muxc->deselect)
|
||||||
|
muxc->deselect(muxc, priv->chan_id);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -65,17 +101,18 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
|
|||||||
int size, union i2c_smbus_data *data)
|
int size, union i2c_smbus_data *data)
|
||||||
{
|
{
|
||||||
struct i2c_mux_priv *priv = adap->algo_data;
|
struct i2c_mux_priv *priv = adap->algo_data;
|
||||||
struct i2c_adapter *parent = priv->parent;
|
struct i2c_mux_core *muxc = priv->muxc;
|
||||||
|
struct i2c_adapter *parent = muxc->parent;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Select the right mux port and perform the transfer. */
|
/* Select the right mux port and perform the transfer. */
|
||||||
|
|
||||||
ret = priv->select(parent, priv->mux_priv, priv->chan_id);
|
ret = muxc->select(muxc, priv->chan_id);
|
||||||
if (ret >= 0)
|
if (ret >= 0)
|
||||||
ret = parent->algo->smbus_xfer(parent, addr, flags,
|
ret = i2c_smbus_xfer(parent, addr, flags,
|
||||||
read_write, command, size, data);
|
read_write, command, size, data);
|
||||||
if (priv->deselect)
|
if (muxc->deselect)
|
||||||
priv->deselect(parent, priv->mux_priv, priv->chan_id);
|
muxc->deselect(muxc, priv->chan_id);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -84,7 +121,7 @@ static int i2c_mux_smbus_xfer(struct i2c_adapter *adap,
|
|||||||
static u32 i2c_mux_functionality(struct i2c_adapter *adap)
|
static u32 i2c_mux_functionality(struct i2c_adapter *adap)
|
||||||
{
|
{
|
||||||
struct i2c_mux_priv *priv = adap->algo_data;
|
struct i2c_mux_priv *priv = adap->algo_data;
|
||||||
struct i2c_adapter *parent = priv->parent;
|
struct i2c_adapter *parent = priv->muxc->parent;
|
||||||
|
|
||||||
return parent->algo->functionality(parent);
|
return parent->algo->functionality(parent);
|
||||||
}
|
}
|
||||||
@@ -102,38 +139,167 @@ static unsigned int i2c_mux_parent_classes(struct i2c_adapter *parent)
|
|||||||
return class;
|
return class;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
static void i2c_mux_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
|
||||||
struct device *mux_dev,
|
|
||||||
void *mux_priv, u32 force_nr, u32 chan_id,
|
|
||||||
unsigned int class,
|
|
||||||
int (*select) (struct i2c_adapter *,
|
|
||||||
void *, u32),
|
|
||||||
int (*deselect) (struct i2c_adapter *,
|
|
||||||
void *, u32))
|
|
||||||
{
|
{
|
||||||
|
struct i2c_mux_priv *priv = adapter->algo_data;
|
||||||
|
struct i2c_adapter *parent = priv->muxc->parent;
|
||||||
|
|
||||||
|
rt_mutex_lock(&parent->mux_lock);
|
||||||
|
if (!(flags & I2C_LOCK_ROOT_ADAPTER))
|
||||||
|
return;
|
||||||
|
i2c_lock_bus(parent, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_mux_trylock_bus(struct i2c_adapter *adapter, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct i2c_mux_priv *priv = adapter->algo_data;
|
||||||
|
struct i2c_adapter *parent = priv->muxc->parent;
|
||||||
|
|
||||||
|
if (!rt_mutex_trylock(&parent->mux_lock))
|
||||||
|
return 0; /* mux_lock not locked, failure */
|
||||||
|
if (!(flags & I2C_LOCK_ROOT_ADAPTER))
|
||||||
|
return 1; /* we only want mux_lock, success */
|
||||||
|
if (parent->trylock_bus(parent, flags))
|
||||||
|
return 1; /* parent locked too, success */
|
||||||
|
rt_mutex_unlock(&parent->mux_lock);
|
||||||
|
return 0; /* parent not locked, failure */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_mux_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
|
||||||
|
{
|
||||||
|
struct i2c_mux_priv *priv = adapter->algo_data;
|
||||||
|
struct i2c_adapter *parent = priv->muxc->parent;
|
||||||
|
|
||||||
|
if (flags & I2C_LOCK_ROOT_ADAPTER)
|
||||||
|
i2c_unlock_bus(parent, flags);
|
||||||
|
rt_mutex_unlock(&parent->mux_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_parent_lock_bus(struct i2c_adapter *adapter,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
struct i2c_mux_priv *priv = adapter->algo_data;
|
||||||
|
struct i2c_adapter *parent = priv->muxc->parent;
|
||||||
|
|
||||||
|
rt_mutex_lock(&parent->mux_lock);
|
||||||
|
i2c_lock_bus(parent, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int i2c_parent_trylock_bus(struct i2c_adapter *adapter,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
struct i2c_mux_priv *priv = adapter->algo_data;
|
||||||
|
struct i2c_adapter *parent = priv->muxc->parent;
|
||||||
|
|
||||||
|
if (!rt_mutex_trylock(&parent->mux_lock))
|
||||||
|
return 0; /* mux_lock not locked, failure */
|
||||||
|
if (parent->trylock_bus(parent, flags))
|
||||||
|
return 1; /* parent locked too, success */
|
||||||
|
rt_mutex_unlock(&parent->mux_lock);
|
||||||
|
return 0; /* parent not locked, failure */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void i2c_parent_unlock_bus(struct i2c_adapter *adapter,
|
||||||
|
unsigned int flags)
|
||||||
|
{
|
||||||
|
struct i2c_mux_priv *priv = adapter->algo_data;
|
||||||
|
struct i2c_adapter *parent = priv->muxc->parent;
|
||||||
|
|
||||||
|
i2c_unlock_bus(parent, flags);
|
||||||
|
rt_mutex_unlock(&parent->mux_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i2c_adapter *i2c_root_adapter(struct device *dev)
|
||||||
|
{
|
||||||
|
struct device *i2c;
|
||||||
|
struct i2c_adapter *i2c_root;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Walk up the device tree to find an i2c adapter, indicating
|
||||||
|
* that this is an i2c client device. Check all ancestors to
|
||||||
|
* handle mfd devices etc.
|
||||||
|
*/
|
||||||
|
for (i2c = dev; i2c; i2c = i2c->parent) {
|
||||||
|
if (i2c->type == &i2c_adapter_type)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!i2c)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Continue up the tree to find the root i2c adapter */
|
||||||
|
i2c_root = to_i2c_adapter(i2c);
|
||||||
|
while (i2c_parent_is_i2c_adapter(i2c_root))
|
||||||
|
i2c_root = i2c_parent_is_i2c_adapter(i2c_root);
|
||||||
|
|
||||||
|
return i2c_root;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_root_adapter);
|
||||||
|
|
||||||
|
struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
|
||||||
|
struct device *dev, int max_adapters,
|
||||||
|
int sizeof_priv, u32 flags,
|
||||||
|
int (*select)(struct i2c_mux_core *, u32),
|
||||||
|
int (*deselect)(struct i2c_mux_core *, u32))
|
||||||
|
{
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
|
|
||||||
|
muxc = devm_kzalloc(dev, sizeof(*muxc)
|
||||||
|
+ max_adapters * sizeof(muxc->adapter[0])
|
||||||
|
+ sizeof_priv, GFP_KERNEL);
|
||||||
|
if (!muxc)
|
||||||
|
return NULL;
|
||||||
|
if (sizeof_priv)
|
||||||
|
muxc->priv = &muxc->adapter[max_adapters];
|
||||||
|
|
||||||
|
muxc->parent = parent;
|
||||||
|
muxc->dev = dev;
|
||||||
|
if (flags & I2C_MUX_LOCKED)
|
||||||
|
muxc->mux_locked = true;
|
||||||
|
muxc->select = select;
|
||||||
|
muxc->deselect = deselect;
|
||||||
|
muxc->max_adapters = max_adapters;
|
||||||
|
|
||||||
|
return muxc;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(i2c_mux_alloc);
|
||||||
|
|
||||||
|
int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
|
||||||
|
u32 force_nr, u32 chan_id,
|
||||||
|
unsigned int class)
|
||||||
|
{
|
||||||
|
struct i2c_adapter *parent = muxc->parent;
|
||||||
struct i2c_mux_priv *priv;
|
struct i2c_mux_priv *priv;
|
||||||
char symlink_name[20];
|
char symlink_name[20];
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
priv = kzalloc(sizeof(struct i2c_mux_priv), GFP_KERNEL);
|
if (muxc->num_adapters >= muxc->max_adapters) {
|
||||||
|
dev_err(muxc->dev, "No room for more i2c-mux adapters\n");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
|
||||||
if (!priv)
|
if (!priv)
|
||||||
return NULL;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Set up private adapter data */
|
/* Set up private adapter data */
|
||||||
priv->parent = parent;
|
priv->muxc = muxc;
|
||||||
priv->mux_dev = mux_dev;
|
|
||||||
priv->mux_priv = mux_priv;
|
|
||||||
priv->chan_id = chan_id;
|
priv->chan_id = chan_id;
|
||||||
priv->select = select;
|
|
||||||
priv->deselect = deselect;
|
|
||||||
|
|
||||||
/* Need to do algo dynamically because we don't know ahead
|
/* Need to do algo dynamically because we don't know ahead
|
||||||
* of time what sort of physical adapter we'll be dealing with.
|
* of time what sort of physical adapter we'll be dealing with.
|
||||||
*/
|
*/
|
||||||
if (parent->algo->master_xfer)
|
if (parent->algo->master_xfer) {
|
||||||
priv->algo.master_xfer = i2c_mux_master_xfer;
|
if (muxc->mux_locked)
|
||||||
if (parent->algo->smbus_xfer)
|
priv->algo.master_xfer = i2c_mux_master_xfer;
|
||||||
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
|
else
|
||||||
|
priv->algo.master_xfer = __i2c_mux_master_xfer;
|
||||||
|
}
|
||||||
|
if (parent->algo->smbus_xfer) {
|
||||||
|
if (muxc->mux_locked)
|
||||||
|
priv->algo.smbus_xfer = i2c_mux_smbus_xfer;
|
||||||
|
else
|
||||||
|
priv->algo.smbus_xfer = __i2c_mux_smbus_xfer;
|
||||||
|
}
|
||||||
priv->algo.functionality = i2c_mux_functionality;
|
priv->algo.functionality = i2c_mux_functionality;
|
||||||
|
|
||||||
/* Now fill out new adapter structure */
|
/* Now fill out new adapter structure */
|
||||||
@@ -146,6 +312,15 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
|||||||
priv->adap.retries = parent->retries;
|
priv->adap.retries = parent->retries;
|
||||||
priv->adap.timeout = parent->timeout;
|
priv->adap.timeout = parent->timeout;
|
||||||
priv->adap.quirks = parent->quirks;
|
priv->adap.quirks = parent->quirks;
|
||||||
|
if (muxc->mux_locked) {
|
||||||
|
priv->adap.lock_bus = i2c_mux_lock_bus;
|
||||||
|
priv->adap.trylock_bus = i2c_mux_trylock_bus;
|
||||||
|
priv->adap.unlock_bus = i2c_mux_unlock_bus;
|
||||||
|
} else {
|
||||||
|
priv->adap.lock_bus = i2c_parent_lock_bus;
|
||||||
|
priv->adap.trylock_bus = i2c_parent_trylock_bus;
|
||||||
|
priv->adap.unlock_bus = i2c_parent_unlock_bus;
|
||||||
|
}
|
||||||
|
|
||||||
/* Sanity check on class */
|
/* Sanity check on class */
|
||||||
if (i2c_mux_parent_classes(parent) & class)
|
if (i2c_mux_parent_classes(parent) & class)
|
||||||
@@ -159,11 +334,11 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
|||||||
* Try to populate the mux adapter's of_node, expands to
|
* Try to populate the mux adapter's of_node, expands to
|
||||||
* nothing if !CONFIG_OF.
|
* nothing if !CONFIG_OF.
|
||||||
*/
|
*/
|
||||||
if (mux_dev->of_node) {
|
if (muxc->dev->of_node) {
|
||||||
struct device_node *child;
|
struct device_node *child;
|
||||||
u32 reg;
|
u32 reg;
|
||||||
|
|
||||||
for_each_child_of_node(mux_dev->of_node, child) {
|
for_each_child_of_node(muxc->dev->of_node, child) {
|
||||||
ret = of_property_read_u32(child, "reg", ®);
|
ret = of_property_read_u32(child, "reg", ®);
|
||||||
if (ret)
|
if (ret)
|
||||||
continue;
|
continue;
|
||||||
@@ -177,8 +352,9 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
|||||||
/*
|
/*
|
||||||
* Associate the mux channel with an ACPI node.
|
* Associate the mux channel with an ACPI node.
|
||||||
*/
|
*/
|
||||||
if (has_acpi_companion(mux_dev))
|
if (has_acpi_companion(muxc->dev))
|
||||||
acpi_preset_companion(&priv->adap.dev, ACPI_COMPANION(mux_dev),
|
acpi_preset_companion(&priv->adap.dev,
|
||||||
|
ACPI_COMPANION(muxc->dev),
|
||||||
chan_id);
|
chan_id);
|
||||||
|
|
||||||
if (force_nr) {
|
if (force_nr) {
|
||||||
@@ -192,35 +368,45 @@ struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
|||||||
"failed to add mux-adapter (error=%d)\n",
|
"failed to add mux-adapter (error=%d)\n",
|
||||||
ret);
|
ret);
|
||||||
kfree(priv);
|
kfree(priv);
|
||||||
return NULL;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
WARN(sysfs_create_link(&priv->adap.dev.kobj, &mux_dev->kobj, "mux_device"),
|
WARN(sysfs_create_link(&priv->adap.dev.kobj, &muxc->dev->kobj,
|
||||||
"can't create symlink to mux device\n");
|
"mux_device"),
|
||||||
|
"can't create symlink to mux device\n");
|
||||||
|
|
||||||
snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id);
|
snprintf(symlink_name, sizeof(symlink_name), "channel-%u", chan_id);
|
||||||
WARN(sysfs_create_link(&mux_dev->kobj, &priv->adap.dev.kobj, symlink_name),
|
WARN(sysfs_create_link(&muxc->dev->kobj, &priv->adap.dev.kobj,
|
||||||
"can't create symlink for channel %u\n", chan_id);
|
symlink_name),
|
||||||
|
"can't create symlink for channel %u\n", chan_id);
|
||||||
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
|
dev_info(&parent->dev, "Added multiplexed i2c bus %d\n",
|
||||||
i2c_adapter_id(&priv->adap));
|
i2c_adapter_id(&priv->adap));
|
||||||
|
|
||||||
return &priv->adap;
|
muxc->adapter[muxc->num_adapters++] = &priv->adap;
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_add_mux_adapter);
|
EXPORT_SYMBOL_GPL(i2c_mux_add_adapter);
|
||||||
|
|
||||||
void i2c_del_mux_adapter(struct i2c_adapter *adap)
|
void i2c_mux_del_adapters(struct i2c_mux_core *muxc)
|
||||||
{
|
{
|
||||||
struct i2c_mux_priv *priv = adap->algo_data;
|
|
||||||
char symlink_name[20];
|
char symlink_name[20];
|
||||||
|
|
||||||
snprintf(symlink_name, sizeof(symlink_name), "channel-%u", priv->chan_id);
|
while (muxc->num_adapters) {
|
||||||
sysfs_remove_link(&priv->mux_dev->kobj, symlink_name);
|
struct i2c_adapter *adap = muxc->adapter[--muxc->num_adapters];
|
||||||
|
struct i2c_mux_priv *priv = adap->algo_data;
|
||||||
|
|
||||||
sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
|
muxc->adapter[muxc->num_adapters] = NULL;
|
||||||
i2c_del_adapter(adap);
|
|
||||||
kfree(priv);
|
snprintf(symlink_name, sizeof(symlink_name),
|
||||||
|
"channel-%u", priv->chan_id);
|
||||||
|
sysfs_remove_link(&muxc->dev->kobj, symlink_name);
|
||||||
|
|
||||||
|
sysfs_remove_link(&priv->adap.dev.kobj, "mux_device");
|
||||||
|
i2c_del_adapter(adap);
|
||||||
|
kfree(priv);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(i2c_del_mux_adapter);
|
EXPORT_SYMBOL_GPL(i2c_mux_del_adapters);
|
||||||
|
|
||||||
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
|
MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>");
|
||||||
MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses");
|
MODULE_DESCRIPTION("I2C driver for multiplexed I2C busses");
|
||||||
|
@@ -28,8 +28,6 @@
|
|||||||
/**
|
/**
|
||||||
* struct i2c_arbitrator_data - Driver data for I2C arbitrator
|
* struct i2c_arbitrator_data - Driver data for I2C arbitrator
|
||||||
*
|
*
|
||||||
* @parent: Parent adapter
|
|
||||||
* @child: Child bus
|
|
||||||
* @our_gpio: GPIO we'll use to claim.
|
* @our_gpio: GPIO we'll use to claim.
|
||||||
* @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
|
* @our_gpio_release: 0 if active high; 1 if active low; AKA if the GPIO ==
|
||||||
* this then consider it released.
|
* this then consider it released.
|
||||||
@@ -42,8 +40,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
struct i2c_arbitrator_data {
|
struct i2c_arbitrator_data {
|
||||||
struct i2c_adapter *parent;
|
|
||||||
struct i2c_adapter *child;
|
|
||||||
int our_gpio;
|
int our_gpio;
|
||||||
int our_gpio_release;
|
int our_gpio_release;
|
||||||
int their_gpio;
|
int their_gpio;
|
||||||
@@ -59,9 +55,9 @@ struct i2c_arbitrator_data {
|
|||||||
*
|
*
|
||||||
* Use the GPIO-based signalling protocol; return -EBUSY if we fail.
|
* Use the GPIO-based signalling protocol; return -EBUSY if we fail.
|
||||||
*/
|
*/
|
||||||
static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan)
|
static int i2c_arbitrator_select(struct i2c_mux_core *muxc, u32 chan)
|
||||||
{
|
{
|
||||||
const struct i2c_arbitrator_data *arb = data;
|
const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
|
||||||
unsigned long stop_retry, stop_time;
|
unsigned long stop_retry, stop_time;
|
||||||
|
|
||||||
/* Start a round of trying to claim the bus */
|
/* Start a round of trying to claim the bus */
|
||||||
@@ -93,7 +89,7 @@ static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan)
|
|||||||
/* Give up, release our claim */
|
/* Give up, release our claim */
|
||||||
gpio_set_value(arb->our_gpio, arb->our_gpio_release);
|
gpio_set_value(arb->our_gpio, arb->our_gpio_release);
|
||||||
udelay(arb->slew_delay_us);
|
udelay(arb->slew_delay_us);
|
||||||
dev_err(&adap->dev, "Could not claim bus, timeout\n");
|
dev_err(muxc->dev, "Could not claim bus, timeout\n");
|
||||||
return -EBUSY;
|
return -EBUSY;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -102,10 +98,9 @@ static int i2c_arbitrator_select(struct i2c_adapter *adap, void *data, u32 chan)
|
|||||||
*
|
*
|
||||||
* Release the I2C bus using the GPIO-based signalling protocol.
|
* Release the I2C bus using the GPIO-based signalling protocol.
|
||||||
*/
|
*/
|
||||||
static int i2c_arbitrator_deselect(struct i2c_adapter *adap, void *data,
|
static int i2c_arbitrator_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||||
u32 chan)
|
|
||||||
{
|
{
|
||||||
const struct i2c_arbitrator_data *arb = data;
|
const struct i2c_arbitrator_data *arb = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
/* Release the bus and wait for the other master to notice */
|
/* Release the bus and wait for the other master to notice */
|
||||||
gpio_set_value(arb->our_gpio, arb->our_gpio_release);
|
gpio_set_value(arb->our_gpio, arb->our_gpio_release);
|
||||||
@@ -119,6 +114,7 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *np = dev->of_node;
|
struct device_node *np = dev->of_node;
|
||||||
struct device_node *parent_np;
|
struct device_node *parent_np;
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct i2c_arbitrator_data *arb;
|
struct i2c_arbitrator_data *arb;
|
||||||
enum of_gpio_flags gpio_flags;
|
enum of_gpio_flags gpio_flags;
|
||||||
unsigned long out_init;
|
unsigned long out_init;
|
||||||
@@ -134,12 +130,13 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
arb = devm_kzalloc(dev, sizeof(*arb), GFP_KERNEL);
|
muxc = i2c_mux_alloc(NULL, dev, 1, sizeof(*arb), 0,
|
||||||
if (!arb) {
|
i2c_arbitrator_select, i2c_arbitrator_deselect);
|
||||||
dev_err(dev, "Cannot allocate i2c_arbitrator_data\n");
|
if (!muxc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
arb = i2c_mux_priv(muxc);
|
||||||
platform_set_drvdata(pdev, arb);
|
|
||||||
|
platform_set_drvdata(pdev, muxc);
|
||||||
|
|
||||||
/* Request GPIOs */
|
/* Request GPIOs */
|
||||||
ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags);
|
ret = of_get_named_gpio_flags(np, "our-claim-gpio", 0, &gpio_flags);
|
||||||
@@ -196,21 +193,18 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
|
|||||||
dev_err(dev, "Cannot parse i2c-parent\n");
|
dev_err(dev, "Cannot parse i2c-parent\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
arb->parent = of_get_i2c_adapter_by_node(parent_np);
|
muxc->parent = of_get_i2c_adapter_by_node(parent_np);
|
||||||
of_node_put(parent_np);
|
of_node_put(parent_np);
|
||||||
if (!arb->parent) {
|
if (!muxc->parent) {
|
||||||
dev_err(dev, "Cannot find parent bus\n");
|
dev_err(dev, "Cannot find parent bus\n");
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Actually add the mux adapter */
|
/* Actually add the mux adapter */
|
||||||
arb->child = i2c_add_mux_adapter(arb->parent, dev, arb, 0, 0, 0,
|
ret = i2c_mux_add_adapter(muxc, 0, 0, 0);
|
||||||
i2c_arbitrator_select,
|
if (ret) {
|
||||||
i2c_arbitrator_deselect);
|
|
||||||
if (!arb->child) {
|
|
||||||
dev_err(dev, "Failed to add adapter\n");
|
dev_err(dev, "Failed to add adapter\n");
|
||||||
ret = -ENODEV;
|
i2c_put_adapter(muxc->parent);
|
||||||
i2c_put_adapter(arb->parent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@@ -218,11 +212,10 @@ static int i2c_arbitrator_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
static int i2c_arbitrator_remove(struct platform_device *pdev)
|
static int i2c_arbitrator_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct i2c_arbitrator_data *arb = platform_get_drvdata(pdev);
|
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
||||||
|
|
||||||
i2c_del_mux_adapter(arb->child);
|
|
||||||
i2c_put_adapter(arb->parent);
|
|
||||||
|
|
||||||
|
i2c_mux_del_adapters(muxc);
|
||||||
|
i2c_put_adapter(muxc->parent);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -15,11 +15,10 @@
|
|||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
|
#include "../../gpio/gpiolib.h"
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
|
|
||||||
struct gpiomux {
|
struct gpiomux {
|
||||||
struct i2c_adapter *parent;
|
|
||||||
struct i2c_adapter **adap; /* child busses */
|
|
||||||
struct i2c_mux_gpio_platform_data data;
|
struct i2c_mux_gpio_platform_data data;
|
||||||
unsigned gpio_base;
|
unsigned gpio_base;
|
||||||
};
|
};
|
||||||
@@ -33,18 +32,18 @@ static void i2c_mux_gpio_set(const struct gpiomux *mux, unsigned val)
|
|||||||
val & (1 << i));
|
val & (1 << i));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_mux_gpio_select(struct i2c_adapter *adap, void *data, u32 chan)
|
static int i2c_mux_gpio_select(struct i2c_mux_core *muxc, u32 chan)
|
||||||
{
|
{
|
||||||
struct gpiomux *mux = data;
|
struct gpiomux *mux = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
i2c_mux_gpio_set(mux, chan);
|
i2c_mux_gpio_set(mux, chan);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_mux_gpio_deselect(struct i2c_adapter *adap, void *data, u32 chan)
|
static int i2c_mux_gpio_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||||
{
|
{
|
||||||
struct gpiomux *mux = data;
|
struct gpiomux *mux = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
i2c_mux_gpio_set(mux, mux->data.idle);
|
i2c_mux_gpio_set(mux, mux->data.idle);
|
||||||
|
|
||||||
@@ -136,19 +135,16 @@ static int i2c_mux_gpio_probe_dt(struct gpiomux *mux,
|
|||||||
|
|
||||||
static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct gpiomux *mux;
|
struct gpiomux *mux;
|
||||||
struct i2c_adapter *parent;
|
struct i2c_adapter *parent;
|
||||||
int (*deselect) (struct i2c_adapter *, void *, u32);
|
struct i2c_adapter *root;
|
||||||
unsigned initial_state, gpio_base;
|
unsigned initial_state, gpio_base;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
||||||
if (!mux) {
|
if (!mux)
|
||||||
dev_err(&pdev->dev, "Cannot allocate gpiomux structure");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, mux);
|
|
||||||
|
|
||||||
if (!dev_get_platdata(&pdev->dev)) {
|
if (!dev_get_platdata(&pdev->dev)) {
|
||||||
ret = i2c_mux_gpio_probe_dt(mux, pdev);
|
ret = i2c_mux_gpio_probe_dt(mux, pdev);
|
||||||
@@ -180,27 +176,32 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||||||
if (!parent)
|
if (!parent)
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
mux->parent = parent;
|
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0,
|
||||||
mux->gpio_base = gpio_base;
|
i2c_mux_gpio_select, NULL);
|
||||||
|
if (!muxc) {
|
||||||
mux->adap = devm_kzalloc(&pdev->dev,
|
|
||||||
sizeof(*mux->adap) * mux->data.n_values,
|
|
||||||
GFP_KERNEL);
|
|
||||||
if (!mux->adap) {
|
|
||||||
dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure");
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto alloc_failed;
|
goto alloc_failed;
|
||||||
}
|
}
|
||||||
|
muxc->priv = mux;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, muxc);
|
||||||
|
|
||||||
|
root = i2c_root_adapter(&parent->dev);
|
||||||
|
|
||||||
|
muxc->mux_locked = true;
|
||||||
|
mux->gpio_base = gpio_base;
|
||||||
|
|
||||||
if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
|
if (mux->data.idle != I2C_MUX_GPIO_NO_IDLE) {
|
||||||
initial_state = mux->data.idle;
|
initial_state = mux->data.idle;
|
||||||
deselect = i2c_mux_gpio_deselect;
|
muxc->deselect = i2c_mux_gpio_deselect;
|
||||||
} else {
|
} else {
|
||||||
initial_state = mux->data.values[0];
|
initial_state = mux->data.values[0];
|
||||||
deselect = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < mux->data.n_gpios; i++) {
|
for (i = 0; i < mux->data.n_gpios; i++) {
|
||||||
|
struct device *gpio_dev;
|
||||||
|
struct gpio_desc *gpio_desc;
|
||||||
|
|
||||||
ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio");
|
ret = gpio_request(gpio_base + mux->data.gpios[i], "i2c-mux-gpio");
|
||||||
if (ret) {
|
if (ret) {
|
||||||
dev_err(&pdev->dev, "Failed to request GPIO %d\n",
|
dev_err(&pdev->dev, "Failed to request GPIO %d\n",
|
||||||
@@ -217,17 +218,24 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||||||
i++; /* gpio_request above succeeded, so must free */
|
i++; /* gpio_request above succeeded, so must free */
|
||||||
goto err_request_gpio;
|
goto err_request_gpio;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!muxc->mux_locked)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
gpio_desc = gpio_to_desc(gpio_base + mux->data.gpios[i]);
|
||||||
|
gpio_dev = &gpio_desc->gdev->dev;
|
||||||
|
muxc->mux_locked = i2c_root_adapter(gpio_dev) == root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (muxc->mux_locked)
|
||||||
|
dev_info(&pdev->dev, "mux-locked i2c mux\n");
|
||||||
|
|
||||||
for (i = 0; i < mux->data.n_values; i++) {
|
for (i = 0; i < mux->data.n_values; i++) {
|
||||||
u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
|
u32 nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
|
||||||
unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
|
unsigned int class = mux->data.classes ? mux->data.classes[i] : 0;
|
||||||
|
|
||||||
mux->adap[i] = i2c_add_mux_adapter(parent, &pdev->dev, mux, nr,
|
ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
|
||||||
mux->data.values[i], class,
|
if (ret) {
|
||||||
i2c_mux_gpio_select, deselect);
|
|
||||||
if (!mux->adap[i]) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
|
dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
|
||||||
goto add_adapter_failed;
|
goto add_adapter_failed;
|
||||||
}
|
}
|
||||||
@@ -239,8 +247,7 @@ static int i2c_mux_gpio_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
add_adapter_failed:
|
add_adapter_failed:
|
||||||
for (; i > 0; i--)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(mux->adap[i - 1]);
|
|
||||||
i = mux->data.n_gpios;
|
i = mux->data.n_gpios;
|
||||||
err_request_gpio:
|
err_request_gpio:
|
||||||
for (; i > 0; i--)
|
for (; i > 0; i--)
|
||||||
@@ -253,16 +260,16 @@ alloc_failed:
|
|||||||
|
|
||||||
static int i2c_mux_gpio_remove(struct platform_device *pdev)
|
static int i2c_mux_gpio_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct gpiomux *mux = platform_get_drvdata(pdev);
|
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
||||||
|
struct gpiomux *mux = i2c_mux_priv(muxc);
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < mux->data.n_values; i++)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(mux->adap[i]);
|
|
||||||
|
|
||||||
for (i = 0; i < mux->data.n_gpios; i++)
|
for (i = 0; i < mux->data.n_gpios; i++)
|
||||||
gpio_free(mux->gpio_base + mux->data.gpios[i]);
|
gpio_free(mux->gpio_base + mux->data.gpios[i]);
|
||||||
|
|
||||||
i2c_put_adapter(mux->parent);
|
i2c_put_adapter(muxc->parent);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -73,7 +73,7 @@
|
|||||||
#define SELECT_DELAY_LONG 1000
|
#define SELECT_DELAY_LONG 1000
|
||||||
|
|
||||||
struct pca9541 {
|
struct pca9541 {
|
||||||
struct i2c_adapter *mux_adap;
|
struct i2c_client *client;
|
||||||
unsigned long select_timeout;
|
unsigned long select_timeout;
|
||||||
unsigned long arb_timeout;
|
unsigned long arb_timeout;
|
||||||
};
|
};
|
||||||
@@ -217,7 +217,8 @@ static const u8 pca9541_control[16] = {
|
|||||||
*/
|
*/
|
||||||
static int pca9541_arbitrate(struct i2c_client *client)
|
static int pca9541_arbitrate(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct pca9541 *data = i2c_get_clientdata(client);
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
|
struct pca9541 *data = i2c_mux_priv(muxc);
|
||||||
int reg;
|
int reg;
|
||||||
|
|
||||||
reg = pca9541_reg_read(client, PCA9541_CONTROL);
|
reg = pca9541_reg_read(client, PCA9541_CONTROL);
|
||||||
@@ -285,9 +286,10 @@ static int pca9541_arbitrate(struct i2c_client *client)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan)
|
static int pca9541_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||||
{
|
{
|
||||||
struct pca9541 *data = i2c_get_clientdata(client);
|
struct pca9541 *data = i2c_mux_priv(muxc);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long timeout = jiffies + ARB2_TIMEOUT;
|
unsigned long timeout = jiffies + ARB2_TIMEOUT;
|
||||||
/* give up after this time */
|
/* give up after this time */
|
||||||
@@ -309,9 +311,11 @@ static int pca9541_select_chan(struct i2c_adapter *adap, void *client, u32 chan)
|
|||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca9541_release_chan(struct i2c_adapter *adap,
|
static int pca9541_release_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||||
void *client, u32 chan)
|
|
||||||
{
|
{
|
||||||
|
struct pca9541 *data = i2c_mux_priv(muxc);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
pca9541_release_bus(client);
|
pca9541_release_bus(client);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -324,20 +328,13 @@ static int pca9541_probe(struct i2c_client *client,
|
|||||||
{
|
{
|
||||||
struct i2c_adapter *adap = client->adapter;
|
struct i2c_adapter *adap = client->adapter;
|
||||||
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
struct pca954x_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct pca9541 *data;
|
struct pca9541 *data;
|
||||||
int force;
|
int force;
|
||||||
int ret = -ENODEV;
|
int ret;
|
||||||
|
|
||||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
|
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE_DATA))
|
||||||
goto err;
|
return -ENODEV;
|
||||||
|
|
||||||
data = kzalloc(sizeof(struct pca9541), GFP_KERNEL);
|
|
||||||
if (!data) {
|
|
||||||
ret = -ENOMEM;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
i2c_set_clientdata(client, data);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* I2C accesses are unprotected here.
|
* I2C accesses are unprotected here.
|
||||||
@@ -352,34 +349,33 @@ static int pca9541_probe(struct i2c_client *client,
|
|||||||
force = 0;
|
force = 0;
|
||||||
if (pdata)
|
if (pdata)
|
||||||
force = pdata->modes[0].adap_id;
|
force = pdata->modes[0].adap_id;
|
||||||
data->mux_adap = i2c_add_mux_adapter(adap, &client->dev, client,
|
muxc = i2c_mux_alloc(adap, &client->dev, 1, sizeof(*data), 0,
|
||||||
force, 0, 0,
|
pca9541_select_chan, pca9541_release_chan);
|
||||||
pca9541_select_chan,
|
if (!muxc)
|
||||||
pca9541_release_chan);
|
return -ENOMEM;
|
||||||
|
|
||||||
if (data->mux_adap == NULL) {
|
data = i2c_mux_priv(muxc);
|
||||||
|
data->client = client;
|
||||||
|
|
||||||
|
i2c_set_clientdata(client, muxc);
|
||||||
|
|
||||||
|
ret = i2c_mux_add_adapter(muxc, force, 0, 0);
|
||||||
|
if (ret) {
|
||||||
dev_err(&client->dev, "failed to register master selector\n");
|
dev_err(&client->dev, "failed to register master selector\n");
|
||||||
goto exit_free;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_info(&client->dev, "registered master selector for I2C %s\n",
|
dev_info(&client->dev, "registered master selector for I2C %s\n",
|
||||||
client->name);
|
client->name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
exit_free:
|
|
||||||
kfree(data);
|
|
||||||
err:
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca9541_remove(struct i2c_client *client)
|
static int pca9541_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct pca9541 *data = i2c_get_clientdata(client);
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
|
|
||||||
i2c_del_mux_adapter(data->mux_adap);
|
i2c_mux_del_adapters(muxc);
|
||||||
|
|
||||||
kfree(data);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -60,9 +60,10 @@ enum pca_type {
|
|||||||
|
|
||||||
struct pca954x {
|
struct pca954x {
|
||||||
enum pca_type type;
|
enum pca_type type;
|
||||||
struct i2c_adapter *virt_adaps[PCA954X_MAX_NCHANS];
|
|
||||||
|
|
||||||
u8 last_chan; /* last register value */
|
u8 last_chan; /* last register value */
|
||||||
|
u8 deselect;
|
||||||
|
struct i2c_client *client;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct chip_desc {
|
struct chip_desc {
|
||||||
@@ -146,10 +147,10 @@ static int pca954x_reg_write(struct i2c_adapter *adap,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca954x_select_chan(struct i2c_adapter *adap,
|
static int pca954x_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||||
void *client, u32 chan)
|
|
||||||
{
|
{
|
||||||
struct pca954x *data = i2c_get_clientdata(client);
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
const struct chip_desc *chip = &chips[data->type];
|
const struct chip_desc *chip = &chips[data->type];
|
||||||
u8 regval;
|
u8 regval;
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@@ -162,21 +163,24 @@ static int pca954x_select_chan(struct i2c_adapter *adap,
|
|||||||
|
|
||||||
/* Only select the channel if its different from the last channel */
|
/* Only select the channel if its different from the last channel */
|
||||||
if (data->last_chan != regval) {
|
if (data->last_chan != regval) {
|
||||||
ret = pca954x_reg_write(adap, client, regval);
|
ret = pca954x_reg_write(muxc->parent, client, regval);
|
||||||
data->last_chan = regval;
|
data->last_chan = regval;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca954x_deselect_mux(struct i2c_adapter *adap,
|
static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
|
||||||
void *client, u32 chan)
|
|
||||||
{
|
{
|
||||||
struct pca954x *data = i2c_get_clientdata(client);
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
struct i2c_client *client = data->client;
|
||||||
|
|
||||||
|
if (!(data->deselect & (1 << chan)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* Deselect active channel */
|
/* Deselect active channel */
|
||||||
data->last_chan = 0;
|
data->last_chan = 0;
|
||||||
return pca954x_reg_write(adap, client, data->last_chan);
|
return pca954x_reg_write(muxc->parent, client, data->last_chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -191,17 +195,22 @@ static int pca954x_probe(struct i2c_client *client,
|
|||||||
bool idle_disconnect_dt;
|
bool idle_disconnect_dt;
|
||||||
struct gpio_desc *gpio;
|
struct gpio_desc *gpio;
|
||||||
int num, force, class;
|
int num, force, class;
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct pca954x *data;
|
struct pca954x *data;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
if (!i2c_check_functionality(adap, I2C_FUNC_SMBUS_BYTE))
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
|
|
||||||
data = devm_kzalloc(&client->dev, sizeof(struct pca954x), GFP_KERNEL);
|
muxc = i2c_mux_alloc(adap, &client->dev,
|
||||||
if (!data)
|
PCA954X_MAX_NCHANS, sizeof(*data), 0,
|
||||||
|
pca954x_select_chan, pca954x_deselect_mux);
|
||||||
|
if (!muxc)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
data = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
i2c_set_clientdata(client, data);
|
i2c_set_clientdata(client, muxc);
|
||||||
|
data->client = client;
|
||||||
|
|
||||||
/* Get the mux out of reset if a reset GPIO is specified. */
|
/* Get the mux out of reset if a reset GPIO is specified. */
|
||||||
gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
|
gpio = devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_LOW);
|
||||||
@@ -238,16 +247,13 @@ static int pca954x_probe(struct i2c_client *client,
|
|||||||
/* discard unconfigured channels */
|
/* discard unconfigured channels */
|
||||||
break;
|
break;
|
||||||
idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
|
idle_disconnect_pd = pdata->modes[num].deselect_on_exit;
|
||||||
|
data->deselect |= (idle_disconnect_pd
|
||||||
|
|| idle_disconnect_dt) << num;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->virt_adaps[num] =
|
ret = i2c_mux_add_adapter(muxc, force, num, class);
|
||||||
i2c_add_mux_adapter(adap, &client->dev, client,
|
|
||||||
force, num, class, pca954x_select_chan,
|
|
||||||
(idle_disconnect_pd || idle_disconnect_dt)
|
|
||||||
? pca954x_deselect_mux : NULL);
|
|
||||||
|
|
||||||
if (data->virt_adaps[num] == NULL) {
|
if (ret) {
|
||||||
ret = -ENODEV;
|
|
||||||
dev_err(&client->dev,
|
dev_err(&client->dev,
|
||||||
"failed to register multiplexed adapter"
|
"failed to register multiplexed adapter"
|
||||||
" %d as bus %d\n", num, force);
|
" %d as bus %d\n", num, force);
|
||||||
@@ -263,23 +269,15 @@ static int pca954x_probe(struct i2c_client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
virt_reg_failed:
|
virt_reg_failed:
|
||||||
for (num--; num >= 0; num--)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(data->virt_adaps[num]);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int pca954x_remove(struct i2c_client *client)
|
static int pca954x_remove(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct pca954x *data = i2c_get_clientdata(client);
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
const struct chip_desc *chip = &chips[data->type];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < chip->nchans; ++i)
|
|
||||||
if (data->virt_adaps[i]) {
|
|
||||||
i2c_del_mux_adapter(data->virt_adaps[i]);
|
|
||||||
data->virt_adaps[i] = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
i2c_mux_del_adapters(muxc);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,7 +285,8 @@ static int pca954x_remove(struct i2c_client *client)
|
|||||||
static int pca954x_resume(struct device *dev)
|
static int pca954x_resume(struct device *dev)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = to_i2c_client(dev);
|
struct i2c_client *client = to_i2c_client(dev);
|
||||||
struct pca954x *data = i2c_get_clientdata(client);
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
|
struct pca954x *data = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
data->last_chan = 0;
|
data->last_chan = 0;
|
||||||
return i2c_smbus_write_byte(client, 0);
|
return i2c_smbus_write_byte(client, 0);
|
||||||
|
@@ -24,36 +24,32 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/of.h>
|
#include <linux/of.h>
|
||||||
|
#include "../../pinctrl/core.h"
|
||||||
|
|
||||||
struct i2c_mux_pinctrl {
|
struct i2c_mux_pinctrl {
|
||||||
struct device *dev;
|
|
||||||
struct i2c_mux_pinctrl_platform_data *pdata;
|
struct i2c_mux_pinctrl_platform_data *pdata;
|
||||||
struct pinctrl *pinctrl;
|
struct pinctrl *pinctrl;
|
||||||
struct pinctrl_state **states;
|
struct pinctrl_state **states;
|
||||||
struct pinctrl_state *state_idle;
|
struct pinctrl_state *state_idle;
|
||||||
struct i2c_adapter *parent;
|
|
||||||
struct i2c_adapter **busses;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_select(struct i2c_adapter *adap, void *data,
|
static int i2c_mux_pinctrl_select(struct i2c_mux_core *muxc, u32 chan)
|
||||||
u32 chan)
|
|
||||||
{
|
{
|
||||||
struct i2c_mux_pinctrl *mux = data;
|
struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
|
return pinctrl_select_state(mux->pinctrl, mux->states[chan]);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_deselect(struct i2c_adapter *adap, void *data,
|
static int i2c_mux_pinctrl_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||||
u32 chan)
|
|
||||||
{
|
{
|
||||||
struct i2c_mux_pinctrl *mux = data;
|
struct i2c_mux_pinctrl *mux = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
return pinctrl_select_state(mux->pinctrl, mux->state_idle);
|
return pinctrl_select_state(mux->pinctrl, mux->state_idle);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
|
static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
int num_names, i, ret;
|
int num_names, i, ret;
|
||||||
@@ -64,15 +60,12 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL);
|
mux->pdata = devm_kzalloc(&pdev->dev, sizeof(*mux->pdata), GFP_KERNEL);
|
||||||
if (!mux->pdata) {
|
if (!mux->pdata)
|
||||||
dev_err(mux->dev,
|
|
||||||
"Cannot allocate i2c_mux_pinctrl_platform_data\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
num_names = of_property_count_strings(np, "pinctrl-names");
|
num_names = of_property_count_strings(np, "pinctrl-names");
|
||||||
if (num_names < 0) {
|
if (num_names < 0) {
|
||||||
dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n",
|
dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n",
|
||||||
num_names);
|
num_names);
|
||||||
return num_names;
|
return num_names;
|
||||||
}
|
}
|
||||||
@@ -80,23 +73,22 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
|
|||||||
mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev,
|
mux->pdata->pinctrl_states = devm_kzalloc(&pdev->dev,
|
||||||
sizeof(*mux->pdata->pinctrl_states) * num_names,
|
sizeof(*mux->pdata->pinctrl_states) * num_names,
|
||||||
GFP_KERNEL);
|
GFP_KERNEL);
|
||||||
if (!mux->pdata->pinctrl_states) {
|
if (!mux->pdata->pinctrl_states)
|
||||||
dev_err(mux->dev, "Cannot allocate pinctrl_states\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < num_names; i++) {
|
for (i = 0; i < num_names; i++) {
|
||||||
ret = of_property_read_string_index(np, "pinctrl-names", i,
|
ret = of_property_read_string_index(np, "pinctrl-names", i,
|
||||||
&mux->pdata->pinctrl_states[mux->pdata->bus_count]);
|
&mux->pdata->pinctrl_states[mux->pdata->bus_count]);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
dev_err(mux->dev, "Cannot parse pinctrl-names: %d\n",
|
dev_err(&pdev->dev, "Cannot parse pinctrl-names: %d\n",
|
||||||
ret);
|
ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count],
|
if (!strcmp(mux->pdata->pinctrl_states[mux->pdata->bus_count],
|
||||||
"idle")) {
|
"idle")) {
|
||||||
if (i != num_names - 1) {
|
if (i != num_names - 1) {
|
||||||
dev_err(mux->dev, "idle state must be last\n");
|
dev_err(&pdev->dev,
|
||||||
|
"idle state must be last\n");
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
mux->pdata->pinctrl_state_idle = "idle";
|
mux->pdata->pinctrl_state_idle = "idle";
|
||||||
@@ -107,13 +99,13 @@ static int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
|
|||||||
|
|
||||||
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
|
adapter_np = of_parse_phandle(np, "i2c-parent", 0);
|
||||||
if (!adapter_np) {
|
if (!adapter_np) {
|
||||||
dev_err(mux->dev, "Cannot parse i2c-parent\n");
|
dev_err(&pdev->dev, "Cannot parse i2c-parent\n");
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
adapter = of_find_i2c_adapter_by_node(adapter_np);
|
||||||
of_node_put(adapter_np);
|
of_node_put(adapter_np);
|
||||||
if (!adapter) {
|
if (!adapter) {
|
||||||
dev_err(mux->dev, "Cannot find parent bus\n");
|
dev_err(&pdev->dev, "Cannot find parent bus\n");
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
mux->pdata->parent_bus_num = i2c_adapter_id(adapter);
|
mux->pdata->parent_bus_num = i2c_adapter_id(adapter);
|
||||||
@@ -129,21 +121,38 @@ static inline int i2c_mux_pinctrl_parse_dt(struct i2c_mux_pinctrl *mux,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static struct i2c_adapter *i2c_mux_pinctrl_root_adapter(
|
||||||
|
struct pinctrl_state *state)
|
||||||
|
{
|
||||||
|
struct i2c_adapter *root = NULL;
|
||||||
|
struct pinctrl_setting *setting;
|
||||||
|
struct i2c_adapter *pin_root;
|
||||||
|
|
||||||
|
list_for_each_entry(setting, &state->settings, node) {
|
||||||
|
pin_root = i2c_root_adapter(setting->pctldev->dev);
|
||||||
|
if (!pin_root)
|
||||||
|
return NULL;
|
||||||
|
if (!root)
|
||||||
|
root = pin_root;
|
||||||
|
else if (root != pin_root)
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct i2c_mux_pinctrl *mux;
|
struct i2c_mux_pinctrl *mux;
|
||||||
int (*deselect)(struct i2c_adapter *, void *, u32);
|
struct i2c_adapter *root;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
mux = devm_kzalloc(&pdev->dev, sizeof(*mux), GFP_KERNEL);
|
||||||
if (!mux) {
|
if (!mux) {
|
||||||
dev_err(&pdev->dev, "Cannot allocate i2c_mux_pinctrl\n");
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
platform_set_drvdata(pdev, mux);
|
|
||||||
|
|
||||||
mux->dev = &pdev->dev;
|
|
||||||
|
|
||||||
mux->pdata = dev_get_platdata(&pdev->dev);
|
mux->pdata = dev_get_platdata(&pdev->dev);
|
||||||
if (!mux->pdata) {
|
if (!mux->pdata) {
|
||||||
@@ -166,14 +175,15 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
mux->busses = devm_kzalloc(&pdev->dev,
|
muxc = i2c_mux_alloc(NULL, &pdev->dev, mux->pdata->bus_count, 0, 0,
|
||||||
sizeof(*mux->busses) * mux->pdata->bus_count,
|
i2c_mux_pinctrl_select, NULL);
|
||||||
GFP_KERNEL);
|
if (!muxc) {
|
||||||
if (!mux->busses) {
|
|
||||||
dev_err(&pdev->dev, "Cannot allocate busses\n");
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
muxc->priv = mux;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, muxc);
|
||||||
|
|
||||||
mux->pinctrl = devm_pinctrl_get(&pdev->dev);
|
mux->pinctrl = devm_pinctrl_get(&pdev->dev);
|
||||||
if (IS_ERR(mux->pinctrl)) {
|
if (IS_ERR(mux->pinctrl)) {
|
||||||
@@ -184,13 +194,13 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
|||||||
for (i = 0; i < mux->pdata->bus_count; i++) {
|
for (i = 0; i < mux->pdata->bus_count; i++) {
|
||||||
mux->states[i] = pinctrl_lookup_state(mux->pinctrl,
|
mux->states[i] = pinctrl_lookup_state(mux->pinctrl,
|
||||||
mux->pdata->pinctrl_states[i]);
|
mux->pdata->pinctrl_states[i]);
|
||||||
if (IS_ERR(mux->states[i])) {
|
if (IS_ERR(mux->states[i])) {
|
||||||
ret = PTR_ERR(mux->states[i]);
|
ret = PTR_ERR(mux->states[i]);
|
||||||
dev_err(&pdev->dev,
|
dev_err(&pdev->dev,
|
||||||
"Cannot look up pinctrl state %s: %d\n",
|
"Cannot look up pinctrl state %s: %d\n",
|
||||||
mux->pdata->pinctrl_states[i], ret);
|
mux->pdata->pinctrl_states[i], ret);
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (mux->pdata->pinctrl_state_idle) {
|
if (mux->pdata->pinctrl_state_idle) {
|
||||||
mux->state_idle = pinctrl_lookup_state(mux->pinctrl,
|
mux->state_idle = pinctrl_lookup_state(mux->pinctrl,
|
||||||
@@ -203,29 +213,39 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
deselect = i2c_mux_pinctrl_deselect;
|
muxc->deselect = i2c_mux_pinctrl_deselect;
|
||||||
} else {
|
|
||||||
deselect = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mux->parent = i2c_get_adapter(mux->pdata->parent_bus_num);
|
muxc->parent = i2c_get_adapter(mux->pdata->parent_bus_num);
|
||||||
if (!mux->parent) {
|
if (!muxc->parent) {
|
||||||
dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
|
dev_err(&pdev->dev, "Parent adapter (%d) not found\n",
|
||||||
mux->pdata->parent_bus_num);
|
mux->pdata->parent_bus_num);
|
||||||
ret = -EPROBE_DEFER;
|
ret = -EPROBE_DEFER;
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
root = i2c_root_adapter(&muxc->parent->dev);
|
||||||
|
|
||||||
|
muxc->mux_locked = true;
|
||||||
|
for (i = 0; i < mux->pdata->bus_count; i++) {
|
||||||
|
if (root != i2c_mux_pinctrl_root_adapter(mux->states[i])) {
|
||||||
|
muxc->mux_locked = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (muxc->mux_locked && mux->pdata->pinctrl_state_idle &&
|
||||||
|
root != i2c_mux_pinctrl_root_adapter(mux->state_idle))
|
||||||
|
muxc->mux_locked = false;
|
||||||
|
|
||||||
|
if (muxc->mux_locked)
|
||||||
|
dev_info(&pdev->dev, "mux-locked i2c mux\n");
|
||||||
|
|
||||||
for (i = 0; i < mux->pdata->bus_count; i++) {
|
for (i = 0; i < mux->pdata->bus_count; i++) {
|
||||||
u32 bus = mux->pdata->base_bus_num ?
|
u32 bus = mux->pdata->base_bus_num ?
|
||||||
(mux->pdata->base_bus_num + i) : 0;
|
(mux->pdata->base_bus_num + i) : 0;
|
||||||
|
|
||||||
mux->busses[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev,
|
ret = i2c_mux_add_adapter(muxc, bus, i, 0);
|
||||||
mux, bus, i, 0,
|
if (ret) {
|
||||||
i2c_mux_pinctrl_select,
|
|
||||||
deselect);
|
|
||||||
if (!mux->busses[i]) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
|
dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
|
||||||
goto err_del_adapter;
|
goto err_del_adapter;
|
||||||
}
|
}
|
||||||
@@ -234,23 +254,18 @@ static int i2c_mux_pinctrl_probe(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
err_del_adapter:
|
err_del_adapter:
|
||||||
for (; i > 0; i--)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(mux->busses[i - 1]);
|
i2c_put_adapter(muxc->parent);
|
||||||
i2c_put_adapter(mux->parent);
|
|
||||||
err:
|
err:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
|
static int i2c_mux_pinctrl_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct i2c_mux_pinctrl *mux = platform_get_drvdata(pdev);
|
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < mux->pdata->bus_count; i++)
|
|
||||||
i2c_del_mux_adapter(mux->busses[i]);
|
|
||||||
|
|
||||||
i2c_put_adapter(mux->parent);
|
|
||||||
|
|
||||||
|
i2c_mux_del_adapters(muxc);
|
||||||
|
i2c_put_adapter(muxc->parent);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -21,8 +21,6 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
|
||||||
struct regmux {
|
struct regmux {
|
||||||
struct i2c_adapter *parent;
|
|
||||||
struct i2c_adapter **adap; /* child busses */
|
|
||||||
struct i2c_mux_reg_platform_data data;
|
struct i2c_mux_reg_platform_data data;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -64,18 +62,16 @@ static int i2c_mux_reg_set(const struct regmux *mux, unsigned int chan_id)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_mux_reg_select(struct i2c_adapter *adap, void *data,
|
static int i2c_mux_reg_select(struct i2c_mux_core *muxc, u32 chan)
|
||||||
unsigned int chan)
|
|
||||||
{
|
{
|
||||||
struct regmux *mux = data;
|
struct regmux *mux = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
return i2c_mux_reg_set(mux, chan);
|
return i2c_mux_reg_set(mux, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data,
|
static int i2c_mux_reg_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||||
unsigned int chan)
|
|
||||||
{
|
{
|
||||||
struct regmux *mux = data;
|
struct regmux *mux = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
if (mux->data.idle_in_use)
|
if (mux->data.idle_in_use)
|
||||||
return i2c_mux_reg_set(mux, mux->data.idle);
|
return i2c_mux_reg_set(mux, mux->data.idle);
|
||||||
@@ -85,7 +81,7 @@ static int i2c_mux_reg_deselect(struct i2c_adapter *adap, void *data,
|
|||||||
|
|
||||||
#ifdef CONFIG_OF
|
#ifdef CONFIG_OF
|
||||||
static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
struct device_node *adapter_np, *child;
|
struct device_node *adapter_np, *child;
|
||||||
@@ -107,7 +103,6 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
|||||||
if (!adapter)
|
if (!adapter)
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
mux->parent = adapter;
|
|
||||||
mux->data.parent = i2c_adapter_id(adapter);
|
mux->data.parent = i2c_adapter_id(adapter);
|
||||||
put_device(&adapter->dev);
|
put_device(&adapter->dev);
|
||||||
|
|
||||||
@@ -161,7 +156,7 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
||||||
struct platform_device *pdev)
|
struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -169,10 +164,10 @@ static int i2c_mux_reg_probe_dt(struct regmux *mux,
|
|||||||
|
|
||||||
static int i2c_mux_reg_probe(struct platform_device *pdev)
|
static int i2c_mux_reg_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct regmux *mux;
|
struct regmux *mux;
|
||||||
struct i2c_adapter *parent;
|
struct i2c_adapter *parent;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int (*deselect)(struct i2c_adapter *, void *, u32);
|
|
||||||
unsigned int class;
|
unsigned int class;
|
||||||
int i, ret, nr;
|
int i, ret, nr;
|
||||||
|
|
||||||
@@ -180,17 +175,9 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
|
|||||||
if (!mux)
|
if (!mux)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
platform_set_drvdata(pdev, mux);
|
|
||||||
|
|
||||||
if (dev_get_platdata(&pdev->dev)) {
|
if (dev_get_platdata(&pdev->dev)) {
|
||||||
memcpy(&mux->data, dev_get_platdata(&pdev->dev),
|
memcpy(&mux->data, dev_get_platdata(&pdev->dev),
|
||||||
sizeof(mux->data));
|
sizeof(mux->data));
|
||||||
|
|
||||||
parent = i2c_get_adapter(mux->data.parent);
|
|
||||||
if (!parent)
|
|
||||||
return -EPROBE_DEFER;
|
|
||||||
|
|
||||||
mux->parent = parent;
|
|
||||||
} else {
|
} else {
|
||||||
ret = i2c_mux_reg_probe_dt(mux, pdev);
|
ret = i2c_mux_reg_probe_dt(mux, pdev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -199,6 +186,10 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parent = i2c_get_adapter(mux->data.parent);
|
||||||
|
if (!parent)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
if (!mux->data.reg) {
|
if (!mux->data.reg) {
|
||||||
dev_info(&pdev->dev,
|
dev_info(&pdev->dev,
|
||||||
"Register not set, using platform resource\n");
|
"Register not set, using platform resource\n");
|
||||||
@@ -215,55 +206,45 @@ static int i2c_mux_reg_probe(struct platform_device *pdev)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
mux->adap = devm_kzalloc(&pdev->dev,
|
muxc = i2c_mux_alloc(parent, &pdev->dev, mux->data.n_values, 0, 0,
|
||||||
sizeof(*mux->adap) * mux->data.n_values,
|
i2c_mux_reg_select, NULL);
|
||||||
GFP_KERNEL);
|
if (!muxc)
|
||||||
if (!mux->adap) {
|
|
||||||
dev_err(&pdev->dev, "Cannot allocate i2c_adapter structure");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
muxc->priv = mux;
|
||||||
|
|
||||||
|
platform_set_drvdata(pdev, muxc);
|
||||||
|
|
||||||
if (mux->data.idle_in_use)
|
if (mux->data.idle_in_use)
|
||||||
deselect = i2c_mux_reg_deselect;
|
muxc->deselect = i2c_mux_reg_deselect;
|
||||||
else
|
|
||||||
deselect = NULL;
|
|
||||||
|
|
||||||
for (i = 0; i < mux->data.n_values; i++) {
|
for (i = 0; i < mux->data.n_values; i++) {
|
||||||
nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
|
nr = mux->data.base_nr ? (mux->data.base_nr + i) : 0;
|
||||||
class = mux->data.classes ? mux->data.classes[i] : 0;
|
class = mux->data.classes ? mux->data.classes[i] : 0;
|
||||||
|
|
||||||
mux->adap[i] = i2c_add_mux_adapter(mux->parent, &pdev->dev, mux,
|
ret = i2c_mux_add_adapter(muxc, nr, mux->data.values[i], class);
|
||||||
nr, mux->data.values[i],
|
if (ret) {
|
||||||
class, i2c_mux_reg_select,
|
|
||||||
deselect);
|
|
||||||
if (!mux->adap[i]) {
|
|
||||||
ret = -ENODEV;
|
|
||||||
dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
|
dev_err(&pdev->dev, "Failed to add adapter %d\n", i);
|
||||||
goto add_adapter_failed;
|
goto add_adapter_failed;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dev_dbg(&pdev->dev, "%d port mux on %s adapter\n",
|
dev_dbg(&pdev->dev, "%d port mux on %s adapter\n",
|
||||||
mux->data.n_values, mux->parent->name);
|
mux->data.n_values, muxc->parent->name);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
add_adapter_failed:
|
add_adapter_failed:
|
||||||
for (; i > 0; i--)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(mux->adap[i - 1]);
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int i2c_mux_reg_remove(struct platform_device *pdev)
|
static int i2c_mux_reg_remove(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct regmux *mux = platform_get_drvdata(pdev);
|
struct i2c_mux_core *muxc = platform_get_drvdata(pdev);
|
||||||
int i;
|
|
||||||
|
|
||||||
for (i = 0; i < mux->data.n_values; i++)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(mux->adap[i]);
|
i2c_put_adapter(muxc->parent);
|
||||||
|
|
||||||
i2c_put_adapter(mux->parent);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@@ -183,7 +183,7 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
|
|||||||
} else
|
} else
|
||||||
return 0; /* no secondary addr, which is OK */
|
return 0; /* no secondary addr, which is OK */
|
||||||
}
|
}
|
||||||
st->mux_client = i2c_new_device(st->mux_adapter, &info);
|
st->mux_client = i2c_new_device(st->muxc->adapter[0], &info);
|
||||||
if (!st->mux_client)
|
if (!st->mux_client)
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
@@ -23,7 +23,6 @@
|
|||||||
#include <linux/kfifo.h>
|
#include <linux/kfifo.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/i2c-mux.h>
|
|
||||||
#include <linux/acpi.h>
|
#include <linux/acpi.h>
|
||||||
#include "inv_mpu_iio.h"
|
#include "inv_mpu_iio.h"
|
||||||
|
|
||||||
|
@@ -15,7 +15,6 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
#include <linux/i2c-mux.h>
|
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/module.h>
|
#include <linux/module.h>
|
||||||
#include "inv_mpu_iio.h"
|
#include "inv_mpu_iio.h"
|
||||||
@@ -25,46 +24,16 @@ static const struct regmap_config inv_mpu_regmap_config = {
|
|||||||
.val_bits = 8,
|
.val_bits = 8,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
static int inv_mpu6050_select_bypass(struct i2c_mux_core *muxc, u32 chan_id)
|
||||||
* The i2c read/write needs to happen in unlocked mode. As the parent
|
|
||||||
* adapter is common. If we use locked versions, it will fail as
|
|
||||||
* the mux adapter will lock the parent i2c adapter, while calling
|
|
||||||
* select/deselect functions.
|
|
||||||
*/
|
|
||||||
static int inv_mpu6050_write_reg_unlocked(struct i2c_client *client,
|
|
||||||
u8 reg, u8 d)
|
|
||||||
{
|
{
|
||||||
int ret;
|
struct iio_dev *indio_dev = i2c_mux_priv(muxc);
|
||||||
u8 buf[2] = {reg, d};
|
|
||||||
struct i2c_msg msg[1] = {
|
|
||||||
{
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = 0,
|
|
||||||
.len = sizeof(buf),
|
|
||||||
.buf = buf,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ret = __i2c_transfer(client->adapter, msg, 1);
|
|
||||||
if (ret != 1)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
|
|
||||||
u32 chan_id)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = mux_priv;
|
|
||||||
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
|
|
||||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
/* Use the same mutex which was used everywhere to protect power-op */
|
/* Use the same mutex which was used everywhere to protect power-op */
|
||||||
mutex_lock(&indio_dev->mlock);
|
mutex_lock(&indio_dev->mlock);
|
||||||
if (!st->powerup_count) {
|
if (!st->powerup_count) {
|
||||||
ret = inv_mpu6050_write_reg_unlocked(client,
|
ret = regmap_write(st->map, st->reg->pwr_mgmt_1, 0);
|
||||||
st->reg->pwr_mgmt_1, 0);
|
|
||||||
if (ret)
|
if (ret)
|
||||||
goto write_error;
|
goto write_error;
|
||||||
|
|
||||||
@@ -73,10 +42,9 @@ static int inv_mpu6050_select_bypass(struct i2c_adapter *adap, void *mux_priv,
|
|||||||
}
|
}
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
st->powerup_count++;
|
st->powerup_count++;
|
||||||
ret = inv_mpu6050_write_reg_unlocked(client,
|
ret = regmap_write(st->map, st->reg->int_pin_cfg,
|
||||||
st->reg->int_pin_cfg,
|
INV_MPU6050_INT_PIN_CFG |
|
||||||
INV_MPU6050_INT_PIN_CFG |
|
INV_MPU6050_BIT_BYPASS_EN);
|
||||||
INV_MPU6050_BIT_BYPASS_EN);
|
|
||||||
}
|
}
|
||||||
write_error:
|
write_error:
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(&indio_dev->mlock);
|
||||||
@@ -84,21 +52,18 @@ write_error:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int inv_mpu6050_deselect_bypass(struct i2c_adapter *adap,
|
static int inv_mpu6050_deselect_bypass(struct i2c_mux_core *muxc, u32 chan_id)
|
||||||
void *mux_priv, u32 chan_id)
|
|
||||||
{
|
{
|
||||||
struct i2c_client *client = mux_priv;
|
struct iio_dev *indio_dev = i2c_mux_priv(muxc);
|
||||||
struct iio_dev *indio_dev = dev_get_drvdata(&client->dev);
|
|
||||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
mutex_lock(&indio_dev->mlock);
|
mutex_lock(&indio_dev->mlock);
|
||||||
/* It doesn't really mattter, if any of the calls fails */
|
/* It doesn't really mattter, if any of the calls fails */
|
||||||
inv_mpu6050_write_reg_unlocked(client, st->reg->int_pin_cfg,
|
regmap_write(st->map, st->reg->int_pin_cfg, INV_MPU6050_INT_PIN_CFG);
|
||||||
INV_MPU6050_INT_PIN_CFG);
|
|
||||||
st->powerup_count--;
|
st->powerup_count--;
|
||||||
if (!st->powerup_count)
|
if (!st->powerup_count)
|
||||||
inv_mpu6050_write_reg_unlocked(client, st->reg->pwr_mgmt_1,
|
regmap_write(st->map, st->reg->pwr_mgmt_1,
|
||||||
INV_MPU6050_BIT_SLEEP);
|
INV_MPU6050_BIT_SLEEP);
|
||||||
mutex_unlock(&indio_dev->mlock);
|
mutex_unlock(&indio_dev->mlock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@@ -160,16 +125,18 @@ static int inv_mpu_probe(struct i2c_client *client,
|
|||||||
return result;
|
return result;
|
||||||
|
|
||||||
st = iio_priv(dev_get_drvdata(&client->dev));
|
st = iio_priv(dev_get_drvdata(&client->dev));
|
||||||
st->mux_adapter = i2c_add_mux_adapter(client->adapter,
|
st->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||||
&client->dev,
|
1, 0, I2C_MUX_LOCKED,
|
||||||
client,
|
inv_mpu6050_select_bypass,
|
||||||
0, 0, 0,
|
inv_mpu6050_deselect_bypass);
|
||||||
inv_mpu6050_select_bypass,
|
if (!st->muxc) {
|
||||||
inv_mpu6050_deselect_bypass);
|
result = -ENOMEM;
|
||||||
if (!st->mux_adapter) {
|
|
||||||
result = -ENODEV;
|
|
||||||
goto out_unreg_device;
|
goto out_unreg_device;
|
||||||
}
|
}
|
||||||
|
st->muxc->priv = dev_get_drvdata(&client->dev);
|
||||||
|
result = i2c_mux_add_adapter(st->muxc, 0, 0, 0);
|
||||||
|
if (result)
|
||||||
|
goto out_unreg_device;
|
||||||
|
|
||||||
result = inv_mpu_acpi_create_mux_client(client);
|
result = inv_mpu_acpi_create_mux_client(client);
|
||||||
if (result)
|
if (result)
|
||||||
@@ -178,7 +145,7 @@ static int inv_mpu_probe(struct i2c_client *client,
|
|||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_del_mux:
|
out_del_mux:
|
||||||
i2c_del_mux_adapter(st->mux_adapter);
|
i2c_mux_del_adapters(st->muxc);
|
||||||
out_unreg_device:
|
out_unreg_device:
|
||||||
inv_mpu_core_remove(&client->dev);
|
inv_mpu_core_remove(&client->dev);
|
||||||
return result;
|
return result;
|
||||||
@@ -190,7 +157,7 @@ static int inv_mpu_remove(struct i2c_client *client)
|
|||||||
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
struct inv_mpu6050_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
inv_mpu_acpi_delete_mux_client(client);
|
inv_mpu_acpi_delete_mux_client(client);
|
||||||
i2c_del_mux_adapter(st->mux_adapter);
|
i2c_mux_del_adapters(st->muxc);
|
||||||
|
|
||||||
return inv_mpu_core_remove(&client->dev);
|
return inv_mpu_core_remove(&client->dev);
|
||||||
}
|
}
|
||||||
|
@@ -11,6 +11,7 @@
|
|||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*/
|
*/
|
||||||
#include <linux/i2c.h>
|
#include <linux/i2c.h>
|
||||||
|
#include <linux/i2c-mux.h>
|
||||||
#include <linux/kfifo.h>
|
#include <linux/kfifo.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
@@ -127,7 +128,7 @@ struct inv_mpu6050_state {
|
|||||||
const struct inv_mpu6050_hw *hw;
|
const struct inv_mpu6050_hw *hw;
|
||||||
enum inv_devices chip_type;
|
enum inv_devices chip_type;
|
||||||
spinlock_t time_stamp_lock;
|
spinlock_t time_stamp_lock;
|
||||||
struct i2c_adapter *mux_adapter;
|
struct i2c_mux_core *muxc;
|
||||||
struct i2c_client *mux_client;
|
struct i2c_client *mux_client;
|
||||||
unsigned int powerup_count;
|
unsigned int powerup_count;
|
||||||
struct inv_mpu6050_platform_data plat_data;
|
struct inv_mpu6050_platform_data plat_data;
|
||||||
|
@@ -1251,9 +1251,9 @@ static void m88ds3103_release(struct dvb_frontend *fe)
|
|||||||
i2c_unregister_device(client);
|
i2c_unregister_device(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int m88ds3103_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
|
static int m88ds3103_select(struct i2c_mux_core *muxc, u32 chan)
|
||||||
{
|
{
|
||||||
struct m88ds3103_dev *dev = mux_priv;
|
struct m88ds3103_dev *dev = i2c_mux_priv(muxc);
|
||||||
struct i2c_client *client = dev->client;
|
struct i2c_client *client = dev->client;
|
||||||
int ret;
|
int ret;
|
||||||
struct i2c_msg msg = {
|
struct i2c_msg msg = {
|
||||||
@@ -1374,7 +1374,7 @@ static struct i2c_adapter *m88ds3103_get_i2c_adapter(struct i2c_client *client)
|
|||||||
|
|
||||||
dev_dbg(&client->dev, "\n");
|
dev_dbg(&client->dev, "\n");
|
||||||
|
|
||||||
return dev->i2c_adapter;
|
return dev->muxc->adapter[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int m88ds3103_probe(struct i2c_client *client,
|
static int m88ds3103_probe(struct i2c_client *client,
|
||||||
@@ -1467,13 +1467,16 @@ static int m88ds3103_probe(struct i2c_client *client,
|
|||||||
goto err_kfree;
|
goto err_kfree;
|
||||||
|
|
||||||
/* create mux i2c adapter for tuner */
|
/* create mux i2c adapter for tuner */
|
||||||
dev->i2c_adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
|
dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
|
||||||
dev, 0, 0, 0, m88ds3103_select,
|
m88ds3103_select, NULL);
|
||||||
NULL);
|
if (!dev->muxc) {
|
||||||
if (dev->i2c_adapter == NULL) {
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
goto err_kfree;
|
goto err_kfree;
|
||||||
}
|
}
|
||||||
|
dev->muxc->priv = dev;
|
||||||
|
ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0);
|
||||||
|
if (ret)
|
||||||
|
goto err_kfree;
|
||||||
|
|
||||||
/* create dvb_frontend */
|
/* create dvb_frontend */
|
||||||
memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
|
memcpy(&dev->fe.ops, &m88ds3103_ops, sizeof(struct dvb_frontend_ops));
|
||||||
@@ -1502,7 +1505,7 @@ static int m88ds3103_remove(struct i2c_client *client)
|
|||||||
|
|
||||||
dev_dbg(&client->dev, "\n");
|
dev_dbg(&client->dev, "\n");
|
||||||
|
|
||||||
i2c_del_mux_adapter(dev->i2c_adapter);
|
i2c_mux_del_adapters(dev->muxc);
|
||||||
|
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
return 0;
|
return 0;
|
||||||
|
@@ -42,7 +42,7 @@ struct m88ds3103_dev {
|
|||||||
enum fe_status fe_status;
|
enum fe_status fe_status;
|
||||||
u32 dvbv3_ber; /* for old DVBv3 API read_ber */
|
u32 dvbv3_ber; /* for old DVBv3 API read_ber */
|
||||||
bool warm; /* FW running */
|
bool warm; /* FW running */
|
||||||
struct i2c_adapter *i2c_adapter;
|
struct i2c_mux_core *muxc;
|
||||||
/* auto detect chip id to do different config */
|
/* auto detect chip id to do different config */
|
||||||
u8 chip_id;
|
u8 chip_id;
|
||||||
/* main mclk is calculated for M88RS6000 dynamically */
|
/* main mclk is calculated for M88RS6000 dynamically */
|
||||||
|
@@ -677,9 +677,9 @@ err:
|
|||||||
* adapter lock is already taken by tuner driver.
|
* adapter lock is already taken by tuner driver.
|
||||||
* Gate is closed automatically after single I2C transfer.
|
* Gate is closed automatically after single I2C transfer.
|
||||||
*/
|
*/
|
||||||
static int rtl2830_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id)
|
static int rtl2830_select(struct i2c_mux_core *muxc, u32 chan_id)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = mux_priv;
|
struct i2c_client *client = i2c_mux_priv(muxc);
|
||||||
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
struct rtl2830_dev *dev = i2c_get_clientdata(client);
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@@ -712,7 +712,7 @@ static struct i2c_adapter *rtl2830_get_i2c_adapter(struct i2c_client *client)
|
|||||||
|
|
||||||
dev_dbg(&client->dev, "\n");
|
dev_dbg(&client->dev, "\n");
|
||||||
|
|
||||||
return dev->adapter;
|
return dev->muxc->adapter[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -865,12 +865,16 @@ static int rtl2830_probe(struct i2c_client *client,
|
|||||||
goto err_regmap_exit;
|
goto err_regmap_exit;
|
||||||
|
|
||||||
/* create muxed i2c adapter for tuner */
|
/* create muxed i2c adapter for tuner */
|
||||||
dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
|
dev->muxc = i2c_mux_alloc(client->adapter, &client->dev, 1, 0, 0,
|
||||||
client, 0, 0, 0, rtl2830_select, NULL);
|
rtl2830_select, NULL);
|
||||||
if (dev->adapter == NULL) {
|
if (!dev->muxc) {
|
||||||
ret = -ENODEV;
|
ret = -ENOMEM;
|
||||||
goto err_regmap_exit;
|
goto err_regmap_exit;
|
||||||
}
|
}
|
||||||
|
dev->muxc->priv = client;
|
||||||
|
ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0);
|
||||||
|
if (ret)
|
||||||
|
goto err_regmap_exit;
|
||||||
|
|
||||||
/* create dvb frontend */
|
/* create dvb frontend */
|
||||||
memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops));
|
memcpy(&dev->fe.ops, &rtl2830_ops, sizeof(dev->fe.ops));
|
||||||
@@ -903,7 +907,7 @@ static int rtl2830_remove(struct i2c_client *client)
|
|||||||
/* stop statistics polling */
|
/* stop statistics polling */
|
||||||
cancel_delayed_work_sync(&dev->stat_work);
|
cancel_delayed_work_sync(&dev->stat_work);
|
||||||
|
|
||||||
i2c_del_mux_adapter(dev->adapter);
|
i2c_mux_del_adapters(dev->muxc);
|
||||||
regmap_exit(dev->regmap);
|
regmap_exit(dev->regmap);
|
||||||
kfree(dev);
|
kfree(dev);
|
||||||
|
|
||||||
|
@@ -29,7 +29,7 @@ struct rtl2830_dev {
|
|||||||
struct rtl2830_platform_data *pdata;
|
struct rtl2830_platform_data *pdata;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct i2c_adapter *adapter;
|
struct i2c_mux_core *muxc;
|
||||||
struct dvb_frontend fe;
|
struct dvb_frontend fe;
|
||||||
bool sleeping;
|
bool sleeping;
|
||||||
unsigned long filters;
|
unsigned long filters;
|
||||||
|
@@ -153,43 +153,6 @@ static const struct rtl2832_reg_entry registers[] = {
|
|||||||
[DVBT_REG_4MSEL] = {0x013, 0, 0},
|
[DVBT_REG_4MSEL] = {0x013, 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Our regmap is bypassing I2C adapter lock, thus we do it! */
|
|
||||||
static int rtl2832_bulk_write(struct i2c_client *client, unsigned int reg,
|
|
||||||
const void *val, size_t val_count)
|
|
||||||
{
|
|
||||||
struct rtl2832_dev *dev = i2c_get_clientdata(client);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
|
||||||
ret = regmap_bulk_write(dev->regmap, reg, val, val_count);
|
|
||||||
i2c_unlock_adapter(client->adapter);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rtl2832_update_bits(struct i2c_client *client, unsigned int reg,
|
|
||||||
unsigned int mask, unsigned int val)
|
|
||||||
{
|
|
||||||
struct rtl2832_dev *dev = i2c_get_clientdata(client);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
|
||||||
ret = regmap_update_bits(dev->regmap, reg, mask, val);
|
|
||||||
i2c_unlock_adapter(client->adapter);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rtl2832_bulk_read(struct i2c_client *client, unsigned int reg,
|
|
||||||
void *val, size_t val_count)
|
|
||||||
{
|
|
||||||
struct rtl2832_dev *dev = i2c_get_clientdata(client);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
|
||||||
ret = regmap_bulk_read(dev->regmap, reg, val, val_count);
|
|
||||||
i2c_unlock_adapter(client->adapter);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val)
|
static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = dev->client;
|
struct i2c_client *client = dev->client;
|
||||||
@@ -204,7 +167,7 @@ static int rtl2832_rd_demod_reg(struct rtl2832_dev *dev, int reg, u32 *val)
|
|||||||
len = (msb >> 3) + 1;
|
len = (msb >> 3) + 1;
|
||||||
mask = REG_MASK(msb - lsb);
|
mask = REG_MASK(msb - lsb);
|
||||||
|
|
||||||
ret = rtl2832_bulk_read(client, reg_start_addr, reading, len);
|
ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -234,7 +197,7 @@ static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val)
|
|||||||
len = (msb >> 3) + 1;
|
len = (msb >> 3) + 1;
|
||||||
mask = REG_MASK(msb - lsb);
|
mask = REG_MASK(msb - lsb);
|
||||||
|
|
||||||
ret = rtl2832_bulk_read(client, reg_start_addr, reading, len);
|
ret = regmap_bulk_read(dev->regmap, reg_start_addr, reading, len);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -248,7 +211,7 @@ static int rtl2832_wr_demod_reg(struct rtl2832_dev *dev, int reg, u32 val)
|
|||||||
for (i = 0; i < len; i++)
|
for (i = 0; i < len; i++)
|
||||||
writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
|
writing[i] = (writing_tmp >> ((len - 1 - i) * 8)) & 0xff;
|
||||||
|
|
||||||
ret = rtl2832_bulk_write(client, reg_start_addr, writing, len);
|
ret = regmap_bulk_write(dev->regmap, reg_start_addr, writing, len);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -525,7 +488,8 @@ static int rtl2832_set_frontend(struct dvb_frontend *fe)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (j = 0; j < sizeof(bw_params[0]); j++) {
|
for (j = 0; j < sizeof(bw_params[0]); j++) {
|
||||||
ret = rtl2832_bulk_write(client, 0x11c + j, &bw_params[i][j], 1);
|
ret = regmap_bulk_write(dev->regmap,
|
||||||
|
0x11c + j, &bw_params[i][j], 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
@@ -581,11 +545,11 @@ static int rtl2832_get_frontend(struct dvb_frontend *fe,
|
|||||||
if (dev->sleeping)
|
if (dev->sleeping)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
ret = rtl2832_bulk_read(client, 0x33c, buf, 2);
|
ret = regmap_bulk_read(dev->regmap, 0x33c, buf, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_bulk_read(client, 0x351, &buf[2], 1);
|
ret = regmap_bulk_read(dev->regmap, 0x351, &buf[2], 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -716,7 +680,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
|||||||
/* signal strength */
|
/* signal strength */
|
||||||
if (dev->fe_status & FE_HAS_SIGNAL) {
|
if (dev->fe_status & FE_HAS_SIGNAL) {
|
||||||
/* read digital AGC */
|
/* read digital AGC */
|
||||||
ret = rtl2832_bulk_read(client, 0x305, &u8tmp, 1);
|
ret = regmap_bulk_read(dev->regmap, 0x305, &u8tmp, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -742,7 +706,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
|||||||
{87659938, 87659938, 87885178, 88241743},
|
{87659938, 87659938, 87885178, 88241743},
|
||||||
};
|
};
|
||||||
|
|
||||||
ret = rtl2832_bulk_read(client, 0x33c, &u8tmp, 1);
|
ret = regmap_bulk_read(dev->regmap, 0x33c, &u8tmp, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -754,7 +718,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
|||||||
if (hierarchy > HIERARCHY_NUM - 1)
|
if (hierarchy > HIERARCHY_NUM - 1)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_bulk_read(client, 0x40c, buf, 2);
|
ret = regmap_bulk_read(dev->regmap, 0x40c, buf, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -775,7 +739,7 @@ static int rtl2832_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
|||||||
|
|
||||||
/* BER */
|
/* BER */
|
||||||
if (dev->fe_status & FE_HAS_LOCK) {
|
if (dev->fe_status & FE_HAS_LOCK) {
|
||||||
ret = rtl2832_bulk_read(client, 0x34e, buf, 2);
|
ret = regmap_bulk_read(dev->regmap, 0x34e, buf, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -825,8 +789,6 @@ static int rtl2832_read_ber(struct dvb_frontend *fe, u32 *ber)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* I2C gate/mux/repeater logic
|
* I2C gate/mux/repeater logic
|
||||||
* We must use unlocked __i2c_transfer() here (through regmap) because of I2C
|
|
||||||
* adapter lock is already taken by tuner driver.
|
|
||||||
* There is delay mechanism to avoid unneeded I2C gate open / close. Gate close
|
* There is delay mechanism to avoid unneeded I2C gate open / close. Gate close
|
||||||
* is delayed here a little bit in order to see if there is sequence of I2C
|
* is delayed here a little bit in order to see if there is sequence of I2C
|
||||||
* messages sent to same I2C bus.
|
* messages sent to same I2C bus.
|
||||||
@@ -838,7 +800,7 @@ static void rtl2832_i2c_gate_work(struct work_struct *work)
|
|||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* close gate */
|
/* close gate */
|
||||||
ret = rtl2832_update_bits(dev->client, 0x101, 0x08, 0x00);
|
ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x00);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -847,19 +809,16 @@ err:
|
|||||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rtl2832_select(struct i2c_adapter *adap, void *mux_priv, u32 chan_id)
|
static int rtl2832_select(struct i2c_mux_core *muxc, u32 chan_id)
|
||||||
{
|
{
|
||||||
struct rtl2832_dev *dev = mux_priv;
|
struct rtl2832_dev *dev = i2c_mux_priv(muxc);
|
||||||
struct i2c_client *client = dev->client;
|
struct i2c_client *client = dev->client;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* terminate possible gate closing */
|
/* terminate possible gate closing */
|
||||||
cancel_delayed_work(&dev->i2c_gate_work);
|
cancel_delayed_work(&dev->i2c_gate_work);
|
||||||
|
|
||||||
/*
|
/* open gate */
|
||||||
* I2C adapter lock is already taken and due to that we will use
|
|
||||||
* regmap_update_bits() which does not lock again I2C adapter.
|
|
||||||
*/
|
|
||||||
ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08);
|
ret = regmap_update_bits(dev->regmap, 0x101, 0x08, 0x08);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
@@ -870,10 +829,9 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rtl2832_deselect(struct i2c_adapter *adap, void *mux_priv,
|
static int rtl2832_deselect(struct i2c_mux_core *muxc, u32 chan_id)
|
||||||
u32 chan_id)
|
|
||||||
{
|
{
|
||||||
struct rtl2832_dev *dev = mux_priv;
|
struct rtl2832_dev *dev = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100));
|
schedule_delayed_work(&dev->i2c_gate_work, usecs_to_jiffies(100));
|
||||||
return 0;
|
return 0;
|
||||||
@@ -932,120 +890,6 @@ static bool rtl2832_volatile_reg(struct device *dev, unsigned int reg)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* We implement own I2C access routines for regmap in order to get manual access
|
|
||||||
* to I2C adapter lock, which is needed for I2C mux adapter.
|
|
||||||
*/
|
|
||||||
static int rtl2832_regmap_read(void *context, const void *reg_buf,
|
|
||||||
size_t reg_size, void *val_buf, size_t val_size)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = context;
|
|
||||||
int ret;
|
|
||||||
struct i2c_msg msg[2] = {
|
|
||||||
{
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = 0,
|
|
||||||
.len = reg_size,
|
|
||||||
.buf = (u8 *)reg_buf,
|
|
||||||
}, {
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = I2C_M_RD,
|
|
||||||
.len = val_size,
|
|
||||||
.buf = val_buf,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ret = __i2c_transfer(client->adapter, msg, 2);
|
|
||||||
if (ret != 2) {
|
|
||||||
dev_warn(&client->dev, "i2c reg read failed %d reg %02x\n",
|
|
||||||
ret, *(u8 *)reg_buf);
|
|
||||||
if (ret >= 0)
|
|
||||||
ret = -EREMOTEIO;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rtl2832_regmap_write(void *context, const void *data, size_t count)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = context;
|
|
||||||
int ret;
|
|
||||||
struct i2c_msg msg[1] = {
|
|
||||||
{
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = 0,
|
|
||||||
.len = count,
|
|
||||||
.buf = (u8 *)data,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
ret = __i2c_transfer(client->adapter, msg, 1);
|
|
||||||
if (ret != 1) {
|
|
||||||
dev_warn(&client->dev, "i2c reg write failed %d reg %02x\n",
|
|
||||||
ret, *(u8 *)data);
|
|
||||||
if (ret >= 0)
|
|
||||||
ret = -EREMOTEIO;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int rtl2832_regmap_gather_write(void *context, const void *reg,
|
|
||||||
size_t reg_len, const void *val,
|
|
||||||
size_t val_len)
|
|
||||||
{
|
|
||||||
struct i2c_client *client = context;
|
|
||||||
int ret;
|
|
||||||
u8 buf[256];
|
|
||||||
struct i2c_msg msg[1] = {
|
|
||||||
{
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = 0,
|
|
||||||
.len = 1 + val_len,
|
|
||||||
.buf = buf,
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
buf[0] = *(u8 const *)reg;
|
|
||||||
memcpy(&buf[1], val, val_len);
|
|
||||||
|
|
||||||
ret = __i2c_transfer(client->adapter, msg, 1);
|
|
||||||
if (ret != 1) {
|
|
||||||
dev_warn(&client->dev, "i2c reg write failed %d reg %02x\n",
|
|
||||||
ret, *(u8 const *)reg);
|
|
||||||
if (ret >= 0)
|
|
||||||
ret = -EREMOTEIO;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* FIXME: Hack. Implement own regmap locking in order to silence lockdep
|
|
||||||
* recursive lock warning. That happens when regmap I2C client calls I2C mux
|
|
||||||
* adapter, which leads demod I2C repeater enable via demod regmap. Operation
|
|
||||||
* takes two regmap locks recursively - but those are different regmap instances
|
|
||||||
* in a two different I2C drivers, so it is not deadlock. Proper fix is to make
|
|
||||||
* regmap aware of lockdep.
|
|
||||||
*/
|
|
||||||
static void rtl2832_regmap_lock(void *__dev)
|
|
||||||
{
|
|
||||||
struct rtl2832_dev *dev = __dev;
|
|
||||||
struct i2c_client *client = dev->client;
|
|
||||||
|
|
||||||
dev_dbg(&client->dev, "\n");
|
|
||||||
mutex_lock(&dev->regmap_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void rtl2832_regmap_unlock(void *__dev)
|
|
||||||
{
|
|
||||||
struct rtl2832_dev *dev = __dev;
|
|
||||||
struct i2c_client *client = dev->client;
|
|
||||||
|
|
||||||
dev_dbg(&client->dev, "\n");
|
|
||||||
mutex_unlock(&dev->regmap_mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client)
|
static struct dvb_frontend *rtl2832_get_dvb_frontend(struct i2c_client *client)
|
||||||
{
|
{
|
||||||
struct rtl2832_dev *dev = i2c_get_clientdata(client);
|
struct rtl2832_dev *dev = i2c_get_clientdata(client);
|
||||||
@@ -1059,7 +903,7 @@ static struct i2c_adapter *rtl2832_get_i2c_adapter(struct i2c_client *client)
|
|||||||
struct rtl2832_dev *dev = i2c_get_clientdata(client);
|
struct rtl2832_dev *dev = i2c_get_clientdata(client);
|
||||||
|
|
||||||
dev_dbg(&client->dev, "\n");
|
dev_dbg(&client->dev, "\n");
|
||||||
return dev->i2c_adapter_tuner;
|
return dev->muxc->adapter[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable)
|
static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable)
|
||||||
@@ -1073,29 +917,29 @@ static int rtl2832_slave_ts_ctrl(struct i2c_client *client, bool enable)
|
|||||||
ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
|
ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x0);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_bulk_write(client, 0x10c, "\x5f\xff", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x10c, "\x5f\xff", 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1);
|
ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_bulk_write(client, 0x0bc, "\x18", 1);
|
ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x18", 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_bulk_write(client, 0x192, "\x7f\xf7\xff", 3);
|
ret = regmap_bulk_write(dev->regmap, 0x192, "\x7f\xf7\xff", 3);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
} else {
|
} else {
|
||||||
ret = rtl2832_bulk_write(client, 0x192, "\x00\x0f\xff", 3);
|
ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_bulk_write(client, 0x0bc, "\x08", 1);
|
ret = regmap_bulk_write(dev->regmap, 0x0bc, "\x08", 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x0);
|
ret = rtl2832_wr_demod_reg(dev, DVBT_PIP_ON, 0x0);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_bulk_write(client, 0x10c, "\x00\x00", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x10c, "\x00\x00", 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
|
ret = rtl2832_wr_demod_reg(dev, DVBT_SOFT_RST, 0x1);
|
||||||
@@ -1124,7 +968,7 @@ static int rtl2832_pid_filter_ctrl(struct dvb_frontend *fe, int onoff)
|
|||||||
else
|
else
|
||||||
u8tmp = 0x00;
|
u8tmp = 0x00;
|
||||||
|
|
||||||
ret = rtl2832_update_bits(client, 0x061, 0xc0, u8tmp);
|
ret = regmap_update_bits(dev->regmap, 0x061, 0xc0, u8tmp);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -1159,14 +1003,14 @@ static int rtl2832_pid_filter(struct dvb_frontend *fe, u8 index, u16 pid,
|
|||||||
buf[1] = (dev->filters >> 8) & 0xff;
|
buf[1] = (dev->filters >> 8) & 0xff;
|
||||||
buf[2] = (dev->filters >> 16) & 0xff;
|
buf[2] = (dev->filters >> 16) & 0xff;
|
||||||
buf[3] = (dev->filters >> 24) & 0xff;
|
buf[3] = (dev->filters >> 24) & 0xff;
|
||||||
ret = rtl2832_bulk_write(client, 0x062, buf, 4);
|
ret = regmap_bulk_write(dev->regmap, 0x062, buf, 4);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* add PID */
|
/* add PID */
|
||||||
buf[0] = (pid >> 8) & 0xff;
|
buf[0] = (pid >> 8) & 0xff;
|
||||||
buf[1] = (pid >> 0) & 0xff;
|
buf[1] = (pid >> 0) & 0xff;
|
||||||
ret = rtl2832_bulk_write(client, 0x066 + 2 * index, buf, 2);
|
ret = regmap_bulk_write(dev->regmap, 0x066 + 2 * index, buf, 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -1184,12 +1028,6 @@ static int rtl2832_probe(struct i2c_client *client,
|
|||||||
struct rtl2832_dev *dev;
|
struct rtl2832_dev *dev;
|
||||||
int ret;
|
int ret;
|
||||||
u8 tmp;
|
u8 tmp;
|
||||||
static const struct regmap_bus regmap_bus = {
|
|
||||||
.read = rtl2832_regmap_read,
|
|
||||||
.write = rtl2832_regmap_write,
|
|
||||||
.gather_write = rtl2832_regmap_gather_write,
|
|
||||||
.val_format_endian_default = REGMAP_ENDIAN_NATIVE,
|
|
||||||
};
|
|
||||||
static const struct regmap_range_cfg regmap_range_cfg[] = {
|
static const struct regmap_range_cfg regmap_range_cfg[] = {
|
||||||
{
|
{
|
||||||
.selector_reg = 0x00,
|
.selector_reg = 0x00,
|
||||||
@@ -1218,36 +1056,35 @@ static int rtl2832_probe(struct i2c_client *client,
|
|||||||
dev->sleeping = true;
|
dev->sleeping = true;
|
||||||
INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work);
|
INIT_DELAYED_WORK(&dev->i2c_gate_work, rtl2832_i2c_gate_work);
|
||||||
/* create regmap */
|
/* create regmap */
|
||||||
mutex_init(&dev->regmap_mutex);
|
|
||||||
dev->regmap_config.reg_bits = 8,
|
dev->regmap_config.reg_bits = 8,
|
||||||
dev->regmap_config.val_bits = 8,
|
dev->regmap_config.val_bits = 8,
|
||||||
dev->regmap_config.lock = rtl2832_regmap_lock,
|
|
||||||
dev->regmap_config.unlock = rtl2832_regmap_unlock,
|
|
||||||
dev->regmap_config.lock_arg = dev,
|
|
||||||
dev->regmap_config.volatile_reg = rtl2832_volatile_reg,
|
dev->regmap_config.volatile_reg = rtl2832_volatile_reg,
|
||||||
dev->regmap_config.max_register = 5 * 0x100,
|
dev->regmap_config.max_register = 5 * 0x100,
|
||||||
dev->regmap_config.ranges = regmap_range_cfg,
|
dev->regmap_config.ranges = regmap_range_cfg,
|
||||||
dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg),
|
dev->regmap_config.num_ranges = ARRAY_SIZE(regmap_range_cfg),
|
||||||
dev->regmap_config.cache_type = REGCACHE_NONE,
|
dev->regmap_config.cache_type = REGCACHE_NONE,
|
||||||
dev->regmap = regmap_init(&client->dev, ®map_bus, client,
|
dev->regmap = regmap_init_i2c(client, &dev->regmap_config);
|
||||||
&dev->regmap_config);
|
|
||||||
if (IS_ERR(dev->regmap)) {
|
if (IS_ERR(dev->regmap)) {
|
||||||
ret = PTR_ERR(dev->regmap);
|
ret = PTR_ERR(dev->regmap);
|
||||||
goto err_kfree;
|
goto err_kfree;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check if the demod is there */
|
/* check if the demod is there */
|
||||||
ret = rtl2832_bulk_read(client, 0x000, &tmp, 1);
|
ret = regmap_bulk_read(dev->regmap, 0x000, &tmp, 1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_regmap_exit;
|
goto err_regmap_exit;
|
||||||
|
|
||||||
/* create muxed i2c adapter for demod tuner bus */
|
/* create muxed i2c adapter for demod tuner bus */
|
||||||
dev->i2c_adapter_tuner = i2c_add_mux_adapter(i2c, &i2c->dev, dev,
|
dev->muxc = i2c_mux_alloc(i2c, &i2c->dev, 1, 0, I2C_MUX_LOCKED,
|
||||||
0, 0, 0, rtl2832_select, rtl2832_deselect);
|
rtl2832_select, rtl2832_deselect);
|
||||||
if (dev->i2c_adapter_tuner == NULL) {
|
if (!dev->muxc) {
|
||||||
ret = -ENODEV;
|
ret = -ENOMEM;
|
||||||
goto err_regmap_exit;
|
goto err_regmap_exit;
|
||||||
}
|
}
|
||||||
|
dev->muxc->priv = dev;
|
||||||
|
ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0);
|
||||||
|
if (ret)
|
||||||
|
goto err_regmap_exit;
|
||||||
|
|
||||||
/* create dvb_frontend */
|
/* create dvb_frontend */
|
||||||
memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
|
memcpy(&dev->fe.ops, &rtl2832_ops, sizeof(struct dvb_frontend_ops));
|
||||||
@@ -1259,9 +1096,7 @@ static int rtl2832_probe(struct i2c_client *client,
|
|||||||
pdata->slave_ts_ctrl = rtl2832_slave_ts_ctrl;
|
pdata->slave_ts_ctrl = rtl2832_slave_ts_ctrl;
|
||||||
pdata->pid_filter = rtl2832_pid_filter;
|
pdata->pid_filter = rtl2832_pid_filter;
|
||||||
pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl;
|
pdata->pid_filter_ctrl = rtl2832_pid_filter_ctrl;
|
||||||
pdata->bulk_read = rtl2832_bulk_read;
|
pdata->regmap = dev->regmap;
|
||||||
pdata->bulk_write = rtl2832_bulk_write;
|
|
||||||
pdata->update_bits = rtl2832_update_bits;
|
|
||||||
|
|
||||||
dev_info(&client->dev, "Realtek RTL2832 successfully attached\n");
|
dev_info(&client->dev, "Realtek RTL2832 successfully attached\n");
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1282,7 +1117,7 @@ static int rtl2832_remove(struct i2c_client *client)
|
|||||||
|
|
||||||
cancel_delayed_work_sync(&dev->i2c_gate_work);
|
cancel_delayed_work_sync(&dev->i2c_gate_work);
|
||||||
|
|
||||||
i2c_del_mux_adapter(dev->i2c_adapter_tuner);
|
i2c_mux_del_adapters(dev->muxc);
|
||||||
|
|
||||||
regmap_exit(dev->regmap);
|
regmap_exit(dev->regmap);
|
||||||
|
|
||||||
|
@@ -57,9 +57,7 @@ struct rtl2832_platform_data {
|
|||||||
int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
|
int (*pid_filter)(struct dvb_frontend *, u8, u16, int);
|
||||||
int (*pid_filter_ctrl)(struct dvb_frontend *, int);
|
int (*pid_filter_ctrl)(struct dvb_frontend *, int);
|
||||||
/* private: Register access for SDR module use only */
|
/* private: Register access for SDR module use only */
|
||||||
int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t);
|
struct regmap *regmap;
|
||||||
int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t);
|
|
||||||
int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif /* RTL2832_H */
|
#endif /* RTL2832_H */
|
||||||
|
@@ -33,10 +33,9 @@
|
|||||||
struct rtl2832_dev {
|
struct rtl2832_dev {
|
||||||
struct rtl2832_platform_data *pdata;
|
struct rtl2832_platform_data *pdata;
|
||||||
struct i2c_client *client;
|
struct i2c_client *client;
|
||||||
struct mutex regmap_mutex;
|
|
||||||
struct regmap_config regmap_config;
|
struct regmap_config regmap_config;
|
||||||
struct regmap *regmap;
|
struct regmap *regmap;
|
||||||
struct i2c_adapter *i2c_adapter_tuner;
|
struct i2c_mux_core *muxc;
|
||||||
struct dvb_frontend fe;
|
struct dvb_frontend fe;
|
||||||
enum fe_status fe_status;
|
enum fe_status fe_status;
|
||||||
u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
|
u64 post_bit_error_prev; /* for old DVBv3 read_ber() calculation */
|
||||||
|
@@ -35,6 +35,7 @@
|
|||||||
#include <linux/platform_device.h>
|
#include <linux/platform_device.h>
|
||||||
#include <linux/jiffies.h>
|
#include <linux/jiffies.h>
|
||||||
#include <linux/math64.h>
|
#include <linux/math64.h>
|
||||||
|
#include <linux/regmap.h>
|
||||||
|
|
||||||
static bool rtl2832_sdr_emulated_fmt;
|
static bool rtl2832_sdr_emulated_fmt;
|
||||||
module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644);
|
module_param_named(emulated_formats, rtl2832_sdr_emulated_fmt, bool, 0644);
|
||||||
@@ -119,6 +120,7 @@ struct rtl2832_sdr_dev {
|
|||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
|
struct regmap *regmap;
|
||||||
|
|
||||||
struct video_device vdev;
|
struct video_device vdev;
|
||||||
struct v4l2_device v4l2_dev;
|
struct v4l2_device v4l2_dev;
|
||||||
@@ -163,47 +165,6 @@ struct rtl2832_sdr_dev {
|
|||||||
unsigned long jiffies_next;
|
unsigned long jiffies_next;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* write multiple registers */
|
|
||||||
static int rtl2832_sdr_wr_regs(struct rtl2832_sdr_dev *dev, u16 reg,
|
|
||||||
const u8 *val, int len)
|
|
||||||
{
|
|
||||||
struct platform_device *pdev = dev->pdev;
|
|
||||||
struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
|
|
||||||
struct i2c_client *client = pdata->i2c_client;
|
|
||||||
|
|
||||||
return pdata->bulk_write(client, reg, val, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
#if 0
|
|
||||||
/* read multiple registers */
|
|
||||||
static int rtl2832_sdr_rd_regs(struct rtl2832_sdr_dev *dev, u16 reg, u8 *val,
|
|
||||||
int len)
|
|
||||||
{
|
|
||||||
struct platform_device *pdev = dev->pdev;
|
|
||||||
struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
|
|
||||||
struct i2c_client *client = pdata->i2c_client;
|
|
||||||
|
|
||||||
return pdata->bulk_read(client, reg, val, len);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* write single register */
|
|
||||||
static int rtl2832_sdr_wr_reg(struct rtl2832_sdr_dev *dev, u16 reg, u8 val)
|
|
||||||
{
|
|
||||||
return rtl2832_sdr_wr_regs(dev, reg, &val, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* write single register with mask */
|
|
||||||
static int rtl2832_sdr_wr_reg_mask(struct rtl2832_sdr_dev *dev, u16 reg,
|
|
||||||
u8 val, u8 mask)
|
|
||||||
{
|
|
||||||
struct platform_device *pdev = dev->pdev;
|
|
||||||
struct rtl2832_sdr_platform_data *pdata = pdev->dev.platform_data;
|
|
||||||
struct i2c_client *client = pdata->i2c_client;
|
|
||||||
|
|
||||||
return pdata->update_bits(client, reg, mask, val);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Private functions */
|
/* Private functions */
|
||||||
static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
|
static struct rtl2832_sdr_frame_buf *rtl2832_sdr_get_next_fill_buf(
|
||||||
struct rtl2832_sdr_dev *dev)
|
struct rtl2832_sdr_dev *dev)
|
||||||
@@ -558,11 +519,11 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev)
|
|||||||
|
|
||||||
f_sr = dev->f_adc;
|
f_sr = dev->f_adc;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x00\x00", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x13e, "\x00\x00", 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x00\x00\x00\x00", 4);
|
ret = regmap_bulk_write(dev->regmap, 0x115, "\x00\x00\x00\x00", 4);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -588,7 +549,7 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev)
|
|||||||
buf[1] = (u32tmp >> 8) & 0xff;
|
buf[1] = (u32tmp >> 8) & 0xff;
|
||||||
buf[2] = (u32tmp >> 0) & 0xff;
|
buf[2] = (u32tmp >> 0) & 0xff;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x119, buf, 3);
|
ret = regmap_bulk_write(dev->regmap, 0x119, buf, 3);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -602,15 +563,15 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev)
|
|||||||
u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
|
u8tmp2 = 0xcd; /* enable ADC I, ADC Q */
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_reg(dev, 0x1b1, u8tmp1);
|
ret = regmap_write(dev->regmap, 0x1b1, u8tmp1);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_reg(dev, 0x008, u8tmp2);
|
ret = regmap_write(dev->regmap, 0x008, u8tmp2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_reg(dev, 0x006, 0x80);
|
ret = regmap_write(dev->regmap, 0x006, 0x80);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -621,168 +582,169 @@ static int rtl2832_sdr_set_adc(struct rtl2832_sdr_dev *dev)
|
|||||||
buf[1] = (u32tmp >> 16) & 0xff;
|
buf[1] = (u32tmp >> 16) & 0xff;
|
||||||
buf[2] = (u32tmp >> 8) & 0xff;
|
buf[2] = (u32tmp >> 8) & 0xff;
|
||||||
buf[3] = (u32tmp >> 0) & 0xff;
|
buf[3] = (u32tmp >> 0) & 0xff;
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x19f, buf, 4);
|
ret = regmap_bulk_write(dev->regmap, 0x19f, buf, 4);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* low-pass filter */
|
/* low-pass filter */
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x11c,
|
ret = regmap_bulk_write(dev->regmap, 0x11c,
|
||||||
"\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
|
"\xca\xdc\xd7\xd8\xe0\xf2\x0e\x35\x06\x50\x9c\x0d\x71\x11\x14\x71\x74\x19\x41\xa5",
|
||||||
20);
|
20);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* mode */
|
/* mode */
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x05", 1);
|
ret = regmap_write(dev->regmap, 0x019, 0x05);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x01a, "\x1b\x16\x0d\x06\x01\xff", 6);
|
ret = regmap_bulk_write(dev->regmap, 0x01a,
|
||||||
|
"\x1b\x16\x0d\x06\x01\xff", 6);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* FSM */
|
/* FSM */
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\xf0\x0f", 3);
|
ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\xf0\x0f", 3);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* PID filter */
|
/* PID filter */
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x061, "\x60", 1);
|
ret = regmap_write(dev->regmap, 0x061, 0x60);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* used RF tuner based settings */
|
/* used RF tuner based settings */
|
||||||
switch (pdata->tuner) {
|
switch (pdata->tuner) {
|
||||||
case RTL2832_SDR_TUNER_E4000:
|
case RTL2832_SDR_TUNER_E4000:
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1);
|
ret = regmap_write(dev->regmap, 0x112, 0x5a);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x102, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1);
|
ret = regmap_write(dev->regmap, 0x103, 0x5a);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x30", 1);
|
ret = regmap_write(dev->regmap, 0x1c7, 0x30);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xd0", 1);
|
ret = regmap_write(dev->regmap, 0x104, 0xd0);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
|
ret = regmap_write(dev->regmap, 0x105, 0xbe);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x18", 1);
|
ret = regmap_write(dev->regmap, 0x1c8, 0x18);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
|
ret = regmap_write(dev->regmap, 0x106, 0x35);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1c9, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1ca, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1cb, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x107, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1cd, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1ce, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x108, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x109, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x10a, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x10b, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xd4", 1);
|
ret = regmap_write(dev->regmap, 0x011, 0xd4);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1);
|
ret = regmap_write(dev->regmap, 0x1e5, 0xf0);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1d9, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1db, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x14", 1);
|
ret = regmap_write(dev->regmap, 0x1dd, 0x14);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xec", 1);
|
ret = regmap_write(dev->regmap, 0x1de, 0xec);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1);
|
ret = regmap_write(dev->regmap, 0x1d8, 0x0c);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1);
|
ret = regmap_write(dev->regmap, 0x1e6, 0x02);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1);
|
ret = regmap_write(dev->regmap, 0x1d7, 0x09);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x83", 1);
|
ret = regmap_write(dev->regmap, 0x00d, 0x83);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x010, "\x49", 1);
|
ret = regmap_write(dev->regmap, 0x010, 0x49);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x87", 1);
|
ret = regmap_write(dev->regmap, 0x00d, 0x87);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00d, "\x85", 1);
|
ret = regmap_write(dev->regmap, 0x00d, 0x85);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x013, "\x02", 1);
|
ret = regmap_write(dev->regmap, 0x013, 0x02);
|
||||||
break;
|
break;
|
||||||
case RTL2832_SDR_TUNER_FC0012:
|
case RTL2832_SDR_TUNER_FC0012:
|
||||||
case RTL2832_SDR_TUNER_FC0013:
|
case RTL2832_SDR_TUNER_FC0013:
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1);
|
ret = regmap_write(dev->regmap, 0x112, 0x5a);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x102, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1);
|
ret = regmap_write(dev->regmap, 0x103, 0x5a);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1);
|
ret = regmap_write(dev->regmap, 0x1c7, 0x2c);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1);
|
ret = regmap_write(dev->regmap, 0x104, 0xcc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
|
ret = regmap_write(dev->regmap, 0x105, 0xbe);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1);
|
ret = regmap_write(dev->regmap, 0x1c8, 0x16);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
|
ret = regmap_write(dev->regmap, 0x106, 0x35);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1c9, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1ca, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1cb, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x107, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1cd, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1ce, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x108, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x109, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x10a, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x10b, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xbf", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xbf", 2);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1e5, "\xf0", 1);
|
ret = regmap_write(dev->regmap, 0x1e5, 0xf0);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1d9, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1d9, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1db, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1db, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1dd, "\x11", 1);
|
ret = regmap_write(dev->regmap, 0x1dd, 0x11);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1de, "\xef", 1);
|
ret = regmap_write(dev->regmap, 0x1de, 0xef);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1d8, "\x0c", 1);
|
ret = regmap_write(dev->regmap, 0x1d8, 0x0c);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1e6, "\x02", 1);
|
ret = regmap_write(dev->regmap, 0x1e6, 0x02);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1d7, "\x09", 1);
|
ret = regmap_write(dev->regmap, 0x1d7, 0x09);
|
||||||
break;
|
break;
|
||||||
case RTL2832_SDR_TUNER_R820T:
|
case RTL2832_SDR_TUNER_R820T:
|
||||||
case RTL2832_SDR_TUNER_R828D:
|
case RTL2832_SDR_TUNER_R828D:
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x5a", 1);
|
ret = regmap_write(dev->regmap, 0x112, 0x5a);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x102, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x01", 1);
|
ret = regmap_write(dev->regmap, 0x115, 0x01);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x103, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x24", 1);
|
ret = regmap_write(dev->regmap, 0x1c7, 0x24);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1);
|
ret = regmap_write(dev->regmap, 0x104, 0xcc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
|
ret = regmap_write(dev->regmap, 0x105, 0xbe);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x14", 1);
|
ret = regmap_write(dev->regmap, 0x1c8, 0x14);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
|
ret = regmap_write(dev->regmap, 0x106, 0x35);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1c9, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1ca, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1cb, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x107, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1cd, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1ce, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x108, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x109, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x10a, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x10b, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xf4", 1);
|
ret = regmap_write(dev->regmap, 0x011, 0xf4);
|
||||||
break;
|
break;
|
||||||
case RTL2832_SDR_TUNER_FC2580:
|
case RTL2832_SDR_TUNER_FC2580:
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x112, "\x39", 1);
|
ret = regmap_write(dev->regmap, 0x112, 0x39);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x102, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x102, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x103, "\x5a", 1);
|
ret = regmap_write(dev->regmap, 0x103, 0x5a);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c7, "\x2c", 1);
|
ret = regmap_write(dev->regmap, 0x1c7, 0x2c);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x104, "\xcc", 1);
|
ret = regmap_write(dev->regmap, 0x104, 0xcc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x105, "\xbe", 1);
|
ret = regmap_write(dev->regmap, 0x105, 0xbe);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c8, "\x16", 1);
|
ret = regmap_write(dev->regmap, 0x1c8, 0x16);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x106, "\x35", 1);
|
ret = regmap_write(dev->regmap, 0x106, 0x35);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1c9, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1c9, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ca, "\x21", 1);
|
ret = regmap_write(dev->regmap, 0x1ca, 0x21);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cb, "\x00", 1);
|
ret = regmap_write(dev->regmap, 0x1cb, 0x00);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x107, "\x40", 1);
|
ret = regmap_write(dev->regmap, 0x107, 0x40);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1cd, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1cd, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x1ce, "\x10", 1);
|
ret = regmap_write(dev->regmap, 0x1ce, 0x10);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x108, "\x80", 1);
|
ret = regmap_write(dev->regmap, 0x108, 0x80);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x109, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x109, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10a, "\x9c", 1);
|
ret = regmap_write(dev->regmap, 0x10a, 0x9c);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x10b, "\x7f", 1);
|
ret = regmap_write(dev->regmap, 0x10b, 0x7f);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x00e, "\xfc", 1);
|
ret = regmap_write(dev->regmap, 0x00e, 0xfc);
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x011, "\xe9\xf4", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x011, "\xe9\xf4", 2);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
dev_notice(&pdev->dev, "Unsupported tuner\n");
|
dev_notice(&pdev->dev, "Unsupported tuner\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
/* software reset */
|
/* software reset */
|
||||||
ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x04, 0x04);
|
ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x04);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_reg_mask(dev, 0x101, 0x00, 0x04);
|
ret = regmap_update_bits(dev->regmap, 0x101, 0x04, 0x00);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
err:
|
err:
|
||||||
@@ -797,29 +759,29 @@ static void rtl2832_sdr_unset_adc(struct rtl2832_sdr_dev *dev)
|
|||||||
dev_dbg(&pdev->dev, "\n");
|
dev_dbg(&pdev->dev, "\n");
|
||||||
|
|
||||||
/* PID filter */
|
/* PID filter */
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x061, "\xe0", 1);
|
ret = regmap_write(dev->regmap, 0x061, 0xe0);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* mode */
|
/* mode */
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x019, "\x20", 1);
|
ret = regmap_write(dev->regmap, 0x019, 0x20);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x017, "\x11\x10", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x017, "\x11\x10", 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* FSM */
|
/* FSM */
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x192, "\x00\x0f\xff", 3);
|
ret = regmap_bulk_write(dev->regmap, 0x192, "\x00\x0f\xff", 3);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x13e, "\x40\x00", 2);
|
ret = regmap_bulk_write(dev->regmap, 0x13e, "\x40\x00", 2);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
ret = rtl2832_sdr_wr_regs(dev, 0x115, "\x06\x3f\xce\xcc", 4);
|
ret = regmap_bulk_write(dev->regmap, 0x115, "\x06\x3f\xce\xcc", 4);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
err:
|
err:
|
||||||
@@ -1399,6 +1361,7 @@ static int rtl2832_sdr_probe(struct platform_device *pdev)
|
|||||||
subdev = pdata->v4l2_subdev;
|
subdev = pdata->v4l2_subdev;
|
||||||
dev->v4l2_subdev = pdata->v4l2_subdev;
|
dev->v4l2_subdev = pdata->v4l2_subdev;
|
||||||
dev->pdev = pdev;
|
dev->pdev = pdev;
|
||||||
|
dev->regmap = pdata->regmap;
|
||||||
dev->udev = pdata->dvb_usb_device->udev;
|
dev->udev = pdata->dvb_usb_device->udev;
|
||||||
dev->f_adc = bands_adc[0].rangelow;
|
dev->f_adc = bands_adc[0].rangelow;
|
||||||
dev->f_tuner = bands_fm[0].rangelow;
|
dev->f_tuner = bands_fm[0].rangelow;
|
||||||
|
@@ -56,10 +56,7 @@ struct rtl2832_sdr_platform_data {
|
|||||||
#define RTL2832_SDR_TUNER_R828D 0x2b
|
#define RTL2832_SDR_TUNER_R828D 0x2b
|
||||||
u8 tuner;
|
u8 tuner;
|
||||||
|
|
||||||
struct i2c_client *i2c_client;
|
struct regmap *regmap;
|
||||||
int (*bulk_read)(struct i2c_client *, unsigned int, void *, size_t);
|
|
||||||
int (*bulk_write)(struct i2c_client *, unsigned int, const void *, size_t);
|
|
||||||
int (*update_bits)(struct i2c_client *, unsigned int, unsigned int, unsigned int);
|
|
||||||
struct dvb_frontend *dvb_frontend;
|
struct dvb_frontend *dvb_frontend;
|
||||||
struct v4l2_subdev *v4l2_subdev;
|
struct v4l2_subdev *v4l2_subdev;
|
||||||
struct dvb_usb_device *dvb_usb_device;
|
struct dvb_usb_device *dvb_usb_device;
|
||||||
|
@@ -18,53 +18,23 @@
|
|||||||
|
|
||||||
static const struct dvb_frontend_ops si2168_ops;
|
static const struct dvb_frontend_ops si2168_ops;
|
||||||
|
|
||||||
/* Own I2C adapter locking is needed because of I2C gate logic. */
|
|
||||||
static int si2168_i2c_master_send_unlocked(const struct i2c_client *client,
|
|
||||||
const char *buf, int count)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct i2c_msg msg = {
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = 0,
|
|
||||||
.len = count,
|
|
||||||
.buf = (char *)buf,
|
|
||||||
};
|
|
||||||
|
|
||||||
ret = __i2c_transfer(client->adapter, &msg, 1);
|
|
||||||
return (ret == 1) ? count : ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int si2168_i2c_master_recv_unlocked(const struct i2c_client *client,
|
|
||||||
char *buf, int count)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
struct i2c_msg msg = {
|
|
||||||
.addr = client->addr,
|
|
||||||
.flags = I2C_M_RD,
|
|
||||||
.len = count,
|
|
||||||
.buf = buf,
|
|
||||||
};
|
|
||||||
|
|
||||||
ret = __i2c_transfer(client->adapter, &msg, 1);
|
|
||||||
return (ret == 1) ? count : ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* execute firmware command */
|
/* execute firmware command */
|
||||||
static int si2168_cmd_execute_unlocked(struct i2c_client *client,
|
static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
|
||||||
struct si2168_cmd *cmd)
|
|
||||||
{
|
{
|
||||||
|
struct si2168_dev *dev = i2c_get_clientdata(client);
|
||||||
int ret;
|
int ret;
|
||||||
unsigned long timeout;
|
unsigned long timeout;
|
||||||
|
|
||||||
|
mutex_lock(&dev->i2c_mutex);
|
||||||
|
|
||||||
if (cmd->wlen) {
|
if (cmd->wlen) {
|
||||||
/* write cmd and args for firmware */
|
/* write cmd and args for firmware */
|
||||||
ret = si2168_i2c_master_send_unlocked(client, cmd->args,
|
ret = i2c_master_send(client, cmd->args, cmd->wlen);
|
||||||
cmd->wlen);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err_mutex_unlock;
|
||||||
} else if (ret != cmd->wlen) {
|
} else if (ret != cmd->wlen) {
|
||||||
ret = -EREMOTEIO;
|
ret = -EREMOTEIO;
|
||||||
goto err;
|
goto err_mutex_unlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,13 +43,12 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client,
|
|||||||
#define TIMEOUT 70
|
#define TIMEOUT 70
|
||||||
timeout = jiffies + msecs_to_jiffies(TIMEOUT);
|
timeout = jiffies + msecs_to_jiffies(TIMEOUT);
|
||||||
while (!time_after(jiffies, timeout)) {
|
while (!time_after(jiffies, timeout)) {
|
||||||
ret = si2168_i2c_master_recv_unlocked(client, cmd->args,
|
ret = i2c_master_recv(client, cmd->args, cmd->rlen);
|
||||||
cmd->rlen);
|
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
goto err;
|
goto err_mutex_unlock;
|
||||||
} else if (ret != cmd->rlen) {
|
} else if (ret != cmd->rlen) {
|
||||||
ret = -EREMOTEIO;
|
ret = -EREMOTEIO;
|
||||||
goto err;
|
goto err_mutex_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* firmware ready? */
|
/* firmware ready? */
|
||||||
@@ -94,32 +63,23 @@ static int si2168_cmd_execute_unlocked(struct i2c_client *client,
|
|||||||
/* error bit set? */
|
/* error bit set? */
|
||||||
if ((cmd->args[0] >> 6) & 0x01) {
|
if ((cmd->args[0] >> 6) & 0x01) {
|
||||||
ret = -EREMOTEIO;
|
ret = -EREMOTEIO;
|
||||||
goto err;
|
goto err_mutex_unlock;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!((cmd->args[0] >> 7) & 0x01)) {
|
if (!((cmd->args[0] >> 7) & 0x01)) {
|
||||||
ret = -ETIMEDOUT;
|
ret = -ETIMEDOUT;
|
||||||
goto err;
|
goto err_mutex_unlock;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_unlock(&dev->i2c_mutex);
|
||||||
return 0;
|
return 0;
|
||||||
err:
|
err_mutex_unlock:
|
||||||
|
mutex_unlock(&dev->i2c_mutex);
|
||||||
dev_dbg(&client->dev, "failed=%d\n", ret);
|
dev_dbg(&client->dev, "failed=%d\n", ret);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int si2168_cmd_execute(struct i2c_client *client, struct si2168_cmd *cmd)
|
|
||||||
{
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
i2c_lock_adapter(client->adapter);
|
|
||||||
ret = si2168_cmd_execute_unlocked(client, cmd);
|
|
||||||
i2c_unlock_adapter(client->adapter);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
static int si2168_read_status(struct dvb_frontend *fe, enum fe_status *status)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = fe->demodulator_priv;
|
struct i2c_client *client = fe->demodulator_priv;
|
||||||
@@ -610,14 +570,9 @@ static int si2168_get_tune_settings(struct dvb_frontend *fe,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static int si2168_select(struct i2c_mux_core *muxc, u32 chan)
|
||||||
* I2C gate logic
|
|
||||||
* We must use unlocked I2C I/O because I2C adapter lock is already taken
|
|
||||||
* by the caller (usually tuner driver).
|
|
||||||
*/
|
|
||||||
static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
|
|
||||||
{
|
{
|
||||||
struct i2c_client *client = mux_priv;
|
struct i2c_client *client = i2c_mux_priv(muxc);
|
||||||
int ret;
|
int ret;
|
||||||
struct si2168_cmd cmd;
|
struct si2168_cmd cmd;
|
||||||
|
|
||||||
@@ -625,7 +580,7 @@ static int si2168_select(struct i2c_adapter *adap, void *mux_priv, u32 chan)
|
|||||||
memcpy(cmd.args, "\xc0\x0d\x01", 3);
|
memcpy(cmd.args, "\xc0\x0d\x01", 3);
|
||||||
cmd.wlen = 3;
|
cmd.wlen = 3;
|
||||||
cmd.rlen = 0;
|
cmd.rlen = 0;
|
||||||
ret = si2168_cmd_execute_unlocked(client, &cmd);
|
ret = si2168_cmd_execute(client, &cmd);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -635,9 +590,9 @@ err:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
|
static int si2168_deselect(struct i2c_mux_core *muxc, u32 chan)
|
||||||
{
|
{
|
||||||
struct i2c_client *client = mux_priv;
|
struct i2c_client *client = i2c_mux_priv(muxc);
|
||||||
int ret;
|
int ret;
|
||||||
struct si2168_cmd cmd;
|
struct si2168_cmd cmd;
|
||||||
|
|
||||||
@@ -645,7 +600,7 @@ static int si2168_deselect(struct i2c_adapter *adap, void *mux_priv, u32 chan)
|
|||||||
memcpy(cmd.args, "\xc0\x0d\x00", 3);
|
memcpy(cmd.args, "\xc0\x0d\x00", 3);
|
||||||
cmd.wlen = 3;
|
cmd.wlen = 3;
|
||||||
cmd.rlen = 0;
|
cmd.rlen = 0;
|
||||||
ret = si2168_cmd_execute_unlocked(client, &cmd);
|
ret = si2168_cmd_execute(client, &cmd);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
@@ -708,18 +663,25 @@ static int si2168_probe(struct i2c_client *client,
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mutex_init(&dev->i2c_mutex);
|
||||||
|
|
||||||
/* create mux i2c adapter for tuner */
|
/* create mux i2c adapter for tuner */
|
||||||
dev->adapter = i2c_add_mux_adapter(client->adapter, &client->dev,
|
dev->muxc = i2c_mux_alloc(client->adapter, &client->dev,
|
||||||
client, 0, 0, 0, si2168_select, si2168_deselect);
|
1, 0, I2C_MUX_LOCKED,
|
||||||
if (dev->adapter == NULL) {
|
si2168_select, si2168_deselect);
|
||||||
ret = -ENODEV;
|
if (!dev->muxc) {
|
||||||
|
ret = -ENOMEM;
|
||||||
goto err_kfree;
|
goto err_kfree;
|
||||||
}
|
}
|
||||||
|
dev->muxc->priv = client;
|
||||||
|
ret = i2c_mux_add_adapter(dev->muxc, 0, 0, 0);
|
||||||
|
if (ret)
|
||||||
|
goto err_kfree;
|
||||||
|
|
||||||
/* create dvb_frontend */
|
/* create dvb_frontend */
|
||||||
memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
|
memcpy(&dev->fe.ops, &si2168_ops, sizeof(struct dvb_frontend_ops));
|
||||||
dev->fe.demodulator_priv = client;
|
dev->fe.demodulator_priv = client;
|
||||||
*config->i2c_adapter = dev->adapter;
|
*config->i2c_adapter = dev->muxc->adapter[0];
|
||||||
*config->fe = &dev->fe;
|
*config->fe = &dev->fe;
|
||||||
dev->ts_mode = config->ts_mode;
|
dev->ts_mode = config->ts_mode;
|
||||||
dev->ts_clock_inv = config->ts_clock_inv;
|
dev->ts_clock_inv = config->ts_clock_inv;
|
||||||
@@ -743,7 +705,7 @@ static int si2168_remove(struct i2c_client *client)
|
|||||||
|
|
||||||
dev_dbg(&client->dev, "\n");
|
dev_dbg(&client->dev, "\n");
|
||||||
|
|
||||||
i2c_del_mux_adapter(dev->adapter);
|
i2c_mux_del_adapters(dev->muxc);
|
||||||
|
|
||||||
dev->fe.ops.release = NULL;
|
dev->fe.ops.release = NULL;
|
||||||
dev->fe.demodulator_priv = NULL;
|
dev->fe.demodulator_priv = NULL;
|
||||||
|
@@ -29,7 +29,8 @@
|
|||||||
|
|
||||||
/* state struct */
|
/* state struct */
|
||||||
struct si2168_dev {
|
struct si2168_dev {
|
||||||
struct i2c_adapter *adapter;
|
struct mutex i2c_mutex;
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct dvb_frontend fe;
|
struct dvb_frontend fe;
|
||||||
enum fe_delivery_system delivery_system;
|
enum fe_delivery_system delivery_system;
|
||||||
enum fe_status fe_status;
|
enum fe_status fe_status;
|
||||||
|
@@ -1305,6 +1305,9 @@ int cx231xx_dev_init(struct cx231xx *dev)
|
|||||||
cx231xx_i2c_register(&dev->i2c_bus[1]);
|
cx231xx_i2c_register(&dev->i2c_bus[1]);
|
||||||
cx231xx_i2c_register(&dev->i2c_bus[2]);
|
cx231xx_i2c_register(&dev->i2c_bus[2]);
|
||||||
|
|
||||||
|
errCode = cx231xx_i2c_mux_create(dev);
|
||||||
|
if (errCode < 0)
|
||||||
|
return errCode;
|
||||||
cx231xx_i2c_mux_register(dev, 0);
|
cx231xx_i2c_mux_register(dev, 0);
|
||||||
cx231xx_i2c_mux_register(dev, 1);
|
cx231xx_i2c_mux_register(dev, 1);
|
||||||
|
|
||||||
@@ -1427,8 +1430,7 @@ EXPORT_SYMBOL_GPL(cx231xx_dev_init);
|
|||||||
void cx231xx_dev_uninit(struct cx231xx *dev)
|
void cx231xx_dev_uninit(struct cx231xx *dev)
|
||||||
{
|
{
|
||||||
/* Un Initialize I2C bus */
|
/* Un Initialize I2C bus */
|
||||||
cx231xx_i2c_mux_unregister(dev, 1);
|
cx231xx_i2c_mux_unregister(dev);
|
||||||
cx231xx_i2c_mux_unregister(dev, 0);
|
|
||||||
cx231xx_i2c_unregister(&dev->i2c_bus[2]);
|
cx231xx_i2c_unregister(&dev->i2c_bus[2]);
|
||||||
cx231xx_i2c_unregister(&dev->i2c_bus[1]);
|
cx231xx_i2c_unregister(&dev->i2c_bus[1]);
|
||||||
cx231xx_i2c_unregister(&dev->i2c_bus[0]);
|
cx231xx_i2c_unregister(&dev->i2c_bus[0]);
|
||||||
|
@@ -557,40 +557,41 @@ int cx231xx_i2c_unregister(struct cx231xx_i2c *bus)
|
|||||||
* cx231xx_i2c_mux_select()
|
* cx231xx_i2c_mux_select()
|
||||||
* switch i2c master number 1 between port1 and port3
|
* switch i2c master number 1 between port1 and port3
|
||||||
*/
|
*/
|
||||||
static int cx231xx_i2c_mux_select(struct i2c_adapter *adap,
|
static int cx231xx_i2c_mux_select(struct i2c_mux_core *muxc, u32 chan_id)
|
||||||
void *mux_priv, u32 chan_id)
|
|
||||||
{
|
{
|
||||||
struct cx231xx *dev = mux_priv;
|
struct cx231xx *dev = i2c_mux_priv(muxc);
|
||||||
|
|
||||||
return cx231xx_enable_i2c_port_3(dev, chan_id);
|
return cx231xx_enable_i2c_port_3(dev, chan_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no)
|
int cx231xx_i2c_mux_create(struct cx231xx *dev)
|
||||||
{
|
{
|
||||||
struct i2c_adapter *i2c_parent = &dev->i2c_bus[1].i2c_adap;
|
dev->muxc = i2c_mux_alloc(&dev->i2c_bus[1].i2c_adap, dev->dev, 2, 0, 0,
|
||||||
/* what is the correct mux_dev? */
|
cx231xx_i2c_mux_select, NULL);
|
||||||
struct device *mux_dev = dev->dev;
|
if (!dev->muxc)
|
||||||
|
return -ENOMEM;
|
||||||
dev->i2c_mux_adap[mux_no] = i2c_add_mux_adapter(i2c_parent,
|
dev->muxc->priv = dev;
|
||||||
mux_dev,
|
|
||||||
dev /* mux_priv */,
|
|
||||||
0,
|
|
||||||
mux_no /* chan_id */,
|
|
||||||
0 /* class */,
|
|
||||||
&cx231xx_i2c_mux_select,
|
|
||||||
NULL);
|
|
||||||
|
|
||||||
if (!dev->i2c_mux_adap[mux_no])
|
|
||||||
dev_warn(dev->dev,
|
|
||||||
"i2c mux %d register FAILED\n", mux_no);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void cx231xx_i2c_mux_unregister(struct cx231xx *dev, int mux_no)
|
int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no)
|
||||||
{
|
{
|
||||||
i2c_del_mux_adapter(dev->i2c_mux_adap[mux_no]);
|
int rc;
|
||||||
dev->i2c_mux_adap[mux_no] = NULL;
|
|
||||||
|
rc = i2c_mux_add_adapter(dev->muxc,
|
||||||
|
0,
|
||||||
|
mux_no /* chan_id */,
|
||||||
|
0 /* class */);
|
||||||
|
if (rc)
|
||||||
|
dev_warn(dev->dev,
|
||||||
|
"i2c mux %d register FAILED\n", mux_no);
|
||||||
|
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void cx231xx_i2c_mux_unregister(struct cx231xx *dev)
|
||||||
|
{
|
||||||
|
i2c_mux_del_adapters(dev->muxc);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port)
|
struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port)
|
||||||
@@ -603,9 +604,9 @@ struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port)
|
|||||||
case I2C_2:
|
case I2C_2:
|
||||||
return &dev->i2c_bus[2].i2c_adap;
|
return &dev->i2c_bus[2].i2c_adap;
|
||||||
case I2C_1_MUX_1:
|
case I2C_1_MUX_1:
|
||||||
return dev->i2c_mux_adap[0];
|
return dev->muxc->adapter[0];
|
||||||
case I2C_1_MUX_3:
|
case I2C_1_MUX_3:
|
||||||
return dev->i2c_mux_adap[1];
|
return dev->muxc->adapter[1];
|
||||||
default:
|
default:
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@@ -624,6 +624,7 @@ struct cx231xx {
|
|||||||
|
|
||||||
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
|
/* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
|
||||||
struct cx231xx_i2c i2c_bus[3];
|
struct cx231xx_i2c i2c_bus[3];
|
||||||
|
struct i2c_mux_core *muxc;
|
||||||
struct i2c_adapter *i2c_mux_adap[2];
|
struct i2c_adapter *i2c_mux_adap[2];
|
||||||
|
|
||||||
unsigned int xc_fw_load_done:1;
|
unsigned int xc_fw_load_done:1;
|
||||||
@@ -760,8 +761,9 @@ int cx231xx_reset_analog_tuner(struct cx231xx *dev);
|
|||||||
void cx231xx_do_i2c_scan(struct cx231xx *dev, int i2c_port);
|
void cx231xx_do_i2c_scan(struct cx231xx *dev, int i2c_port);
|
||||||
int cx231xx_i2c_register(struct cx231xx_i2c *bus);
|
int cx231xx_i2c_register(struct cx231xx_i2c *bus);
|
||||||
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
|
int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
|
||||||
|
int cx231xx_i2c_mux_create(struct cx231xx *dev);
|
||||||
int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no);
|
int cx231xx_i2c_mux_register(struct cx231xx *dev, int mux_no);
|
||||||
void cx231xx_i2c_mux_unregister(struct cx231xx *dev, int mux_no);
|
void cx231xx_i2c_mux_unregister(struct cx231xx *dev);
|
||||||
struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port);
|
struct i2c_adapter *cx231xx_get_i2c_adap(struct cx231xx *dev, int i2c_port);
|
||||||
|
|
||||||
/* Internal block control functions */
|
/* Internal block control functions */
|
||||||
|
@@ -1333,10 +1333,7 @@ static int rtl2832u_tuner_attach(struct dvb_usb_adapter *adap)
|
|||||||
case TUNER_RTL2832_R828D:
|
case TUNER_RTL2832_R828D:
|
||||||
pdata.clk = dev->rtl2832_platform_data.clk;
|
pdata.clk = dev->rtl2832_platform_data.clk;
|
||||||
pdata.tuner = dev->tuner;
|
pdata.tuner = dev->tuner;
|
||||||
pdata.i2c_client = dev->i2c_client_demod;
|
pdata.regmap = dev->rtl2832_platform_data.regmap;
|
||||||
pdata.bulk_read = dev->rtl2832_platform_data.bulk_read;
|
|
||||||
pdata.bulk_write = dev->rtl2832_platform_data.bulk_write;
|
|
||||||
pdata.update_bits = dev->rtl2832_platform_data.update_bits;
|
|
||||||
pdata.dvb_frontend = adap->fe[0];
|
pdata.dvb_frontend = adap->fe[0];
|
||||||
pdata.dvb_usb_device = d;
|
pdata.dvb_usb_device = d;
|
||||||
pdata.v4l2_subdev = subdev;
|
pdata.v4l2_subdev = subdev;
|
||||||
|
@@ -245,8 +245,7 @@ static ssize_t at24_eeprom_read(struct at24_data *at24, char *buf,
|
|||||||
if (status == count)
|
if (status == count)
|
||||||
return count;
|
return count;
|
||||||
|
|
||||||
/* REVISIT: at HZ=100, this is sloooow */
|
usleep_range(1000, 1500);
|
||||||
msleep(1);
|
|
||||||
} while (time_before(read_time, timeout));
|
} while (time_before(read_time, timeout));
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
@@ -365,8 +364,7 @@ static ssize_t at24_eeprom_write(struct at24_data *at24, const char *buf,
|
|||||||
if (status == count)
|
if (status == count)
|
||||||
return count;
|
return count;
|
||||||
|
|
||||||
/* REVISIT: at HZ=100, this is sloooow */
|
usleep_range(1000, 1500);
|
||||||
msleep(1);
|
|
||||||
} while (time_before(write_time, timeout));
|
} while (time_before(write_time, timeout));
|
||||||
|
|
||||||
return -ETIMEDOUT;
|
return -ETIMEDOUT;
|
||||||
@@ -544,10 +542,7 @@ static int at24_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
|||||||
} else {
|
} else {
|
||||||
return -EPFNOSUPPORT;
|
return -EPFNOSUPPORT;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/* Use I2C operations unless we're stuck with SMBus extensions. */
|
|
||||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
|
||||||
if (i2c_check_functionality(client->adapter,
|
if (i2c_check_functionality(client->adapter,
|
||||||
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
|
I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
|
||||||
use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA;
|
use_smbus_write = I2C_SMBUS_I2C_BLOCK_DATA;
|
||||||
|
@@ -1692,13 +1692,7 @@ static struct i2c_driver unittest_i2c_dev_driver = {
|
|||||||
|
|
||||||
#if IS_BUILTIN(CONFIG_I2C_MUX)
|
#if IS_BUILTIN(CONFIG_I2C_MUX)
|
||||||
|
|
||||||
struct unittest_i2c_mux_data {
|
static int unittest_i2c_mux_select_chan(struct i2c_mux_core *muxc, u32 chan)
|
||||||
int nchans;
|
|
||||||
struct i2c_adapter *adap[];
|
|
||||||
};
|
|
||||||
|
|
||||||
static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap,
|
|
||||||
void *client, u32 chan)
|
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -1706,11 +1700,11 @@ static int unittest_i2c_mux_select_chan(struct i2c_adapter *adap,
|
|||||||
static int unittest_i2c_mux_probe(struct i2c_client *client,
|
static int unittest_i2c_mux_probe(struct i2c_client *client,
|
||||||
const struct i2c_device_id *id)
|
const struct i2c_device_id *id)
|
||||||
{
|
{
|
||||||
int ret, i, nchans, size;
|
int ret, i, nchans;
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
struct i2c_adapter *adap = to_i2c_adapter(dev->parent);
|
struct i2c_adapter *adap = to_i2c_adapter(dev->parent);
|
||||||
struct device_node *np = client->dev.of_node, *child;
|
struct device_node *np = client->dev.of_node, *child;
|
||||||
struct unittest_i2c_mux_data *stm;
|
struct i2c_mux_core *muxc;
|
||||||
u32 reg, max_reg;
|
u32 reg, max_reg;
|
||||||
|
|
||||||
dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
|
dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
|
||||||
@@ -1734,25 +1728,20 @@ static int unittest_i2c_mux_probe(struct i2c_client *client,
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
size = offsetof(struct unittest_i2c_mux_data, adap[nchans]);
|
muxc = i2c_mux_alloc(adap, dev, nchans, 0, 0,
|
||||||
stm = devm_kzalloc(dev, size, GFP_KERNEL);
|
unittest_i2c_mux_select_chan, NULL);
|
||||||
if (!stm) {
|
if (!muxc)
|
||||||
dev_err(dev, "Out of memory\n");
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
|
||||||
stm->nchans = nchans;
|
|
||||||
for (i = 0; i < nchans; i++) {
|
for (i = 0; i < nchans; i++) {
|
||||||
stm->adap[i] = i2c_add_mux_adapter(adap, dev, client,
|
ret = i2c_mux_add_adapter(muxc, 0, i, 0);
|
||||||
0, i, 0, unittest_i2c_mux_select_chan, NULL);
|
if (ret) {
|
||||||
if (!stm->adap[i]) {
|
|
||||||
dev_err(dev, "Failed to register mux #%d\n", i);
|
dev_err(dev, "Failed to register mux #%d\n", i);
|
||||||
for (i--; i >= 0; i--)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(stm->adap[i]);
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
i2c_set_clientdata(client, stm);
|
i2c_set_clientdata(client, muxc);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
@@ -1761,12 +1750,10 @@ static int unittest_i2c_mux_remove(struct i2c_client *client)
|
|||||||
{
|
{
|
||||||
struct device *dev = &client->dev;
|
struct device *dev = &client->dev;
|
||||||
struct device_node *np = client->dev.of_node;
|
struct device_node *np = client->dev.of_node;
|
||||||
struct unittest_i2c_mux_data *stm = i2c_get_clientdata(client);
|
struct i2c_mux_core *muxc = i2c_get_clientdata(client);
|
||||||
int i;
|
|
||||||
|
|
||||||
dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
|
dev_dbg(dev, "%s for node @%s\n", __func__, np->full_name);
|
||||||
for (i = stm->nchans - 1; i >= 0; i--)
|
i2c_mux_del_adapters(muxc);
|
||||||
i2c_del_mux_adapter(stm->adap[i]);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,22 +27,49 @@
|
|||||||
|
|
||||||
#ifdef __KERNEL__
|
#ifdef __KERNEL__
|
||||||
|
|
||||||
/*
|
#include <linux/bitops.h>
|
||||||
* Called to create a i2c bus on a multiplexed bus segment.
|
|
||||||
* The mux_dev and chan_id parameters are passed to the select
|
|
||||||
* and deselect callback functions to perform hardware-specific
|
|
||||||
* mux control.
|
|
||||||
*/
|
|
||||||
struct i2c_adapter *i2c_add_mux_adapter(struct i2c_adapter *parent,
|
|
||||||
struct device *mux_dev,
|
|
||||||
void *mux_priv, u32 force_nr, u32 chan_id,
|
|
||||||
unsigned int class,
|
|
||||||
int (*select) (struct i2c_adapter *,
|
|
||||||
void *mux_dev, u32 chan_id),
|
|
||||||
int (*deselect) (struct i2c_adapter *,
|
|
||||||
void *mux_dev, u32 chan_id));
|
|
||||||
|
|
||||||
void i2c_del_mux_adapter(struct i2c_adapter *adap);
|
struct i2c_mux_core {
|
||||||
|
struct i2c_adapter *parent;
|
||||||
|
struct device *dev;
|
||||||
|
bool mux_locked;
|
||||||
|
|
||||||
|
void *priv;
|
||||||
|
|
||||||
|
int (*select)(struct i2c_mux_core *, u32 chan_id);
|
||||||
|
int (*deselect)(struct i2c_mux_core *, u32 chan_id);
|
||||||
|
|
||||||
|
int num_adapters;
|
||||||
|
int max_adapters;
|
||||||
|
struct i2c_adapter *adapter[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct i2c_mux_core *i2c_mux_alloc(struct i2c_adapter *parent,
|
||||||
|
struct device *dev, int max_adapters,
|
||||||
|
int sizeof_priv, u32 flags,
|
||||||
|
int (*select)(struct i2c_mux_core *, u32),
|
||||||
|
int (*deselect)(struct i2c_mux_core *, u32));
|
||||||
|
|
||||||
|
/* flags for i2c_mux_alloc */
|
||||||
|
#define I2C_MUX_LOCKED BIT(0)
|
||||||
|
|
||||||
|
static inline void *i2c_mux_priv(struct i2c_mux_core *muxc)
|
||||||
|
{
|
||||||
|
return muxc->priv;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct i2c_adapter *i2c_root_adapter(struct device *dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Called to create an i2c bus on a multiplexed bus segment.
|
||||||
|
* The chan_id parameter is passed to the select and deselect
|
||||||
|
* callback functions to perform hardware-specific mux control.
|
||||||
|
*/
|
||||||
|
int i2c_mux_add_adapter(struct i2c_mux_core *muxc,
|
||||||
|
u32 force_nr, u32 chan_id,
|
||||||
|
unsigned int class);
|
||||||
|
|
||||||
|
void i2c_mux_del_adapters(struct i2c_mux_core *muxc);
|
||||||
|
|
||||||
#endif /* __KERNEL__ */
|
#endif /* __KERNEL__ */
|
||||||
|
|
||||||
|
@@ -524,6 +524,7 @@ struct i2c_adapter {
|
|||||||
|
|
||||||
/* data fields that are valid for all devices */
|
/* data fields that are valid for all devices */
|
||||||
struct rt_mutex bus_lock;
|
struct rt_mutex bus_lock;
|
||||||
|
struct rt_mutex mux_lock;
|
||||||
|
|
||||||
int timeout; /* in jiffies */
|
int timeout; /* in jiffies */
|
||||||
int retries;
|
int retries;
|
||||||
@@ -538,6 +539,10 @@ struct i2c_adapter {
|
|||||||
|
|
||||||
struct i2c_bus_recovery_info *bus_recovery_info;
|
struct i2c_bus_recovery_info *bus_recovery_info;
|
||||||
const struct i2c_adapter_quirks *quirks;
|
const struct i2c_adapter_quirks *quirks;
|
||||||
|
|
||||||
|
void (*lock_bus)(struct i2c_adapter *, unsigned int flags);
|
||||||
|
int (*trylock_bus)(struct i2c_adapter *, unsigned int flags);
|
||||||
|
void (*unlock_bus)(struct i2c_adapter *, unsigned int flags);
|
||||||
};
|
};
|
||||||
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
|
#define to_i2c_adapter(d) container_of(d, struct i2c_adapter, dev)
|
||||||
|
|
||||||
@@ -567,8 +572,44 @@ i2c_parent_is_i2c_adapter(const struct i2c_adapter *adapter)
|
|||||||
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *));
|
int i2c_for_each_dev(void *data, int (*fn)(struct device *, void *));
|
||||||
|
|
||||||
/* Adapter locking functions, exported for shared pin cases */
|
/* Adapter locking functions, exported for shared pin cases */
|
||||||
void i2c_lock_adapter(struct i2c_adapter *);
|
#define I2C_LOCK_ROOT_ADAPTER BIT(0)
|
||||||
void i2c_unlock_adapter(struct i2c_adapter *);
|
#define I2C_LOCK_SEGMENT BIT(1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_lock_bus - Get exclusive access to an I2C bus segment
|
||||||
|
* @adapter: Target I2C bus segment
|
||||||
|
* @flags: I2C_LOCK_ROOT_ADAPTER locks the root i2c adapter, I2C_LOCK_SEGMENT
|
||||||
|
* locks only this branch in the adapter tree
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
i2c_lock_bus(struct i2c_adapter *adapter, unsigned int flags)
|
||||||
|
{
|
||||||
|
adapter->lock_bus(adapter, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* i2c_unlock_bus - Release exclusive access to an I2C bus segment
|
||||||
|
* @adapter: Target I2C bus segment
|
||||||
|
* @flags: I2C_LOCK_ROOT_ADAPTER unlocks the root i2c adapter, I2C_LOCK_SEGMENT
|
||||||
|
* unlocks only this branch in the adapter tree
|
||||||
|
*/
|
||||||
|
static inline void
|
||||||
|
i2c_unlock_bus(struct i2c_adapter *adapter, unsigned int flags)
|
||||||
|
{
|
||||||
|
adapter->unlock_bus(adapter, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
i2c_lock_adapter(struct i2c_adapter *adapter)
|
||||||
|
{
|
||||||
|
i2c_lock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
i2c_unlock_adapter(struct i2c_adapter *adapter)
|
||||||
|
{
|
||||||
|
i2c_unlock_bus(adapter, I2C_LOCK_ROOT_ADAPTER);
|
||||||
|
}
|
||||||
|
|
||||||
/*flags for the client struct: */
|
/*flags for the client struct: */
|
||||||
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
#define I2C_CLIENT_PEC 0x04 /* Use Packet Error Checking */
|
||||||
@@ -654,6 +695,11 @@ static inline int i2c_adapter_id(struct i2c_adapter *adap)
|
|||||||
return adap->nr;
|
return adap->nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg)
|
||||||
|
{
|
||||||
|
return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* module_i2c_driver() - Helper macro for registering a modular I2C driver
|
* module_i2c_driver() - Helper macro for registering a modular I2C driver
|
||||||
* @__i2c_driver: i2c_driver struct
|
* @__i2c_driver: i2c_driver struct
|
||||||
|
@@ -68,14 +68,15 @@
|
|||||||
struct i2c_msg {
|
struct i2c_msg {
|
||||||
__u16 addr; /* slave address */
|
__u16 addr; /* slave address */
|
||||||
__u16 flags;
|
__u16 flags;
|
||||||
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
|
|
||||||
#define I2C_M_RD 0x0001 /* read data, from slave to master */
|
#define I2C_M_RD 0x0001 /* read data, from slave to master */
|
||||||
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
/* I2C_M_RD is guaranteed to be 0x0001! */
|
||||||
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
|
#define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
|
||||||
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
|
||||||
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
|
||||||
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
|
||||||
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
|
#define I2C_M_RECV_LEN 0x0400 /* length will be first received byte */
|
||||||
|
#define I2C_M_NO_RD_ACK 0x0800 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||||
|
#define I2C_M_IGNORE_NAK 0x1000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||||
|
#define I2C_M_REV_DIR_ADDR 0x2000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||||
|
#define I2C_M_NOSTART 0x4000 /* if I2C_FUNC_NOSTART */
|
||||||
|
#define I2C_M_STOP 0x8000 /* if I2C_FUNC_PROTOCOL_MANGLING */
|
||||||
__u16 len; /* msg length */
|
__u16 len; /* msg length */
|
||||||
__u8 *buf; /* pointer to msg data */
|
__u8 *buf; /* pointer to msg data */
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user