mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-03-21 06:24:12 +00:00
IXP4xx driver updates for modernizing the IXP4xx platforms,
taregeted for v5.15: - Add DT bindings to the expansion bus and PATA libata driver. - Add a new expansion bus driver. - Rewrite the watchdog driver to use the watchdog core and spawn from the timer (clocksource) driver. - Refactor the PATA/libata driver to probe from the device tree and use the expansion bus driver to manipulate chip select timings directly. -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEElDRnuGcz/wPCXQWMQRCzN7AZXXMFAmEKauIACgkQQRCzN7AZ XXOuvQ//dUpnPho0e5M+2sKTzM2Gh0o5GvFzzCGHk6CtiF4diifhCTUL8tFdZGM3 ZkYYliksgT0dQ3A0FLndOJmH9xgISkYwOwPh0Cvybolr3JZ2+2btHMFK5j81IYJp OCTC5mX77Ezw+VsqyRfArt/UKQzw0xet46xPZEp2xBJBzRd4zcNVDpNW2yNNYZ89 2wUmxU5YJknvD5WOM7BPDBBPr6FzczmmDlCtpxGiNwKprhTp0vdJJxEEphtjXXim qOAjFMG8H4hZhpM8iK44hbMr5yBbZXUH7nwhEDOiEeQMrDCYOU6sr33rEYOmXUG2 dpOK6NZBrNBSK1rlbZ5FbHYMpMX1fCTBFX5TO3RZvyul8n5t0SBLe1grn2It9yTG wyAwnA0KCdDbg+6Xgn0X/pEvpr4l2W8Up1/CO8wvrDYQXu9YOp+5of2Nm1iBkOF0 w48GoBWMJBlRK8a4UAvqGFskkf1Sh5+P6zCnmUWdVjW4l94QZJ6QNhL25Znj3WhF Mz8+yVCv/LM1lOiWh0AxytejP2DmYgIbXrkc4ctaH5UtR9R5BTT+QtCHDuipxWeC nC5ucPlw2/XKyNxiy9ooBxo19XaK9rmUdagT4ViGxvhlopx20FYslAVMg6XSGruP HE22zsHyZanNMjdmGv6guvQsZm9PC68JBbwv8J0JQQaouR49rAk= =v2E7 -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEo6/YBQwIrVS28WGKmmx57+YAGNkFAmEKnj0ACgkQmmx57+YA GNn+UBAAiZI7jAfykKZ/r0IHlspI88deTvfiDtBsMmPHtRfW67yFsRzKmU/TL21y ae7tPVOMgfPJ+AXLbARh0KnpmIXm++A7lY2PJezth38Gxm5Gx8rGbX41V6w6ZFH4 Zn+lJR6pv+wccqz7QqEWPhKrbvYybCdwYpFjkZUUjyxMjjiWCXvl+M3PaIKtVITj a7t+hLFJ/h6IbBX3igZZG/fXfQh30HSrmaEzA08Uuamn3EUfYof+xfLAZ0xwhmnh v9E5b27yRRhxir6nUFPlqt9+meAqFsK6eqrb5aZpjloBUJas8mkcOnF8Ch0d/0jn TJJMImxU3eoeWi/8LHFSUl65Xxo8hTAwtpC5fMseDh0n0p5aFL5D6gbKeJ89PgeO HRUc1YG05IgtF+70TyldxtLMdZwyBIwFMTIBaJPbT/Zz21LjZbep6sEIZkKGuE8I n5p1tGX2Hjxqfcfk6Ftl+NY4V80BX+kq0esU0RT0Bve0swed22GIq0tH/8680ln8 G61pDuQqVEedxq8WrxfXdptb5iBOkiNI7HgBmvkpw0U986BwGtqFrYBKeFYj/wOQ LGcqWSGaFcofDIySKrTsR8im51CHwKou+hrFusItFlcCjC4DPcMVo0+1BdYfp0Zo vQ09IaHb209uw62YjBQe4MaAQxmm21zzLEqk6LUNd4hyEpOdx2w= =2bLl -----END PGP SIGNATURE----- Merge tag 'ixp4xx-drivers-arm-soc-v5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik into arm/drivers IXP4xx driver updates for modernizing the IXP4xx platforms, taregeted for v5.15: - Add DT bindings to the expansion bus and PATA libata driver. - Add a new expansion bus driver. - Rewrite the watchdog driver to use the watchdog core and spawn from the timer (clocksource) driver. - Refactor the PATA/libata driver to probe from the device tree and use the expansion bus driver to manipulate chip select timings directly. * tag 'ixp4xx-drivers-arm-soc-v5.15-1' of git://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-nomadik: pata: ixp4xx: Rewrite to use device tree pata: ixp4xx: Add DT bindings pata: ixp4xx: Refer to cmd and ctl rather than csN pata: ixp4xx: Use IS_ENABLED() to determine endianness pata: ixp4xx: Use local dev variable watchdog: ixp4xx: Rewrite driver to use core bus: ixp4xx: Add a driver for IXP4xx expansion bus bus: ixp4xx: Add DT bindings for the IXP4xx expansion bus Link: https://lore.kernel.org/r/CACRpkdZaCosXsgp02nuUbd_nEvdxm5-z0+d0oSA97UTWQ0RQQg@mail.gmail.com Signed-off-by: Arnd Bergmann <arnd@arndb.de>
This commit is contained in:
commit
775dea4dee
11 changed files with 1028 additions and 272 deletions
|
@ -0,0 +1,61 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/ata/intel,ixp4xx-compact-flash.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel IXP4xx CompactFlash Card Controller
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
description: |
|
||||
The IXP4xx network processors have a CompactFlash interface that presents
|
||||
a CompactFlash card to the system as a true IDE (parallel ATA) device. The
|
||||
device is always connected to the expansion bus of the IXP4xx SoCs using one
|
||||
or two chip select areas and address translating logic on the board. The
|
||||
node must be placed inside a chip select node on the IXP4xx expansion bus.
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: intel,ixp4xx-compact-flash
|
||||
|
||||
reg:
|
||||
items:
|
||||
- description: Command interface registers
|
||||
- description: Control interface registers
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
allOf:
|
||||
- $ref: pata-common.yaml#
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
bus@c4000000 {
|
||||
compatible = "intel,ixp43x-expansion-bus-controller", "syscon";
|
||||
reg = <0xc4000000 0x1000>;
|
||||
native-endian;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>;
|
||||
dma-ranges = <0 0x0 0x50000000 0x01000000>, <1 0x0 0x51000000 0x01000000>;
|
||||
ide@1,0 {
|
||||
compatible = "intel,ixp4xx-compact-flash";
|
||||
reg = <1 0x00000000 0x1000>, <1 0x00040000 0x1000>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <12 IRQ_TYPE_EDGE_RISING>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -0,0 +1,168 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/bus/intel,ixp4xx-expansion-bus-controller.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Intel IXP4xx Expansion Bus Controller
|
||||
|
||||
description: |
|
||||
The IXP4xx expansion bus controller handles access to devices on the
|
||||
memory-mapped expansion bus on the Intel IXP4xx family of system on chips,
|
||||
including IXP42x, IXP43x, IXP45x and IXP46x.
|
||||
|
||||
maintainers:
|
||||
- Linus Walleij <linus.walleij@linaro.org>
|
||||
|
||||
properties:
|
||||
$nodename:
|
||||
pattern: '^bus@[0-9a-f]+$'
|
||||
|
||||
compatible:
|
||||
items:
|
||||
- enum:
|
||||
- intel,ixp42x-expansion-bus-controller
|
||||
- intel,ixp43x-expansion-bus-controller
|
||||
- intel,ixp45x-expansion-bus-controller
|
||||
- intel,ixp46x-expansion-bus-controller
|
||||
- const: syscon
|
||||
|
||||
reg:
|
||||
description: Control registers for the expansion bus, these are not
|
||||
inside the memory range handled by the expansion bus.
|
||||
maxItems: 1
|
||||
|
||||
native-endian:
|
||||
$ref: /schemas/types.yaml#/definitions/flag
|
||||
description: The IXP4xx has a peculiar MMIO access scheme, as it changes
|
||||
the access pattern for words (swizzling) on the bus depending on whether
|
||||
the SoC is running in big-endian or little-endian mode. Thus the
|
||||
registers must always be accessed using native endianness.
|
||||
|
||||
"#address-cells":
|
||||
description: |
|
||||
The first cell is the chip select number.
|
||||
The second cell is the address offset within the bank.
|
||||
const: 2
|
||||
|
||||
"#size-cells":
|
||||
const: 1
|
||||
|
||||
ranges: true
|
||||
dma-ranges: true
|
||||
|
||||
patternProperties:
|
||||
"^.*@[0-7],[0-9a-f]+$":
|
||||
description: Devices attached to chip selects are represented as
|
||||
subnodes.
|
||||
type: object
|
||||
|
||||
properties:
|
||||
intel,ixp4xx-eb-t1:
|
||||
description: Address timing, extend address phase with n cycles.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 3
|
||||
|
||||
intel,ixp4xx-eb-t2:
|
||||
description: Setup chip select timing, extend setup phase with n cycles.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 3
|
||||
|
||||
intel,ixp4xx-eb-t3:
|
||||
description: Strobe timing, extend strobe phase with n cycles.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 15
|
||||
|
||||
intel,ixp4xx-eb-t4:
|
||||
description: Hold timing, extend hold phase with n cycles.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 3
|
||||
|
||||
intel,ixp4xx-eb-t5:
|
||||
description: Recovery timing, extend recovery phase with n cycles.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
maximum: 15
|
||||
|
||||
intel,ixp4xx-eb-cycle-type:
|
||||
description: The type of cycles to use on the expansion bus for this
|
||||
chip select. 0 = Intel cycles, 1 = Motorola cycles, 2 = HPI cycles.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1, 2]
|
||||
|
||||
intel,ixp4xx-eb-byte-access-on-halfword:
|
||||
description: Allow byte read access on half word devices.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
|
||||
intel,ixp4xx-eb-hpi-hrdy-pol-high:
|
||||
description: Set HPI HRDY polarity to active high when using HPI.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
|
||||
intel,ixp4xx-eb-mux-address-and-data:
|
||||
description: Multiplex address and data on the data bus.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
|
||||
intel,ixp4xx-eb-ahb-split-transfers:
|
||||
description: Enable AHB split transfers.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
|
||||
intel,ixp4xx-eb-write-enable:
|
||||
description: Enable write cycles.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
|
||||
intel,ixp4xx-eb-byte-access:
|
||||
description: Expansion bus uses only 8 bits. The default is to use
|
||||
16 bits.
|
||||
$ref: /schemas/types.yaml#/definitions/uint32
|
||||
enum: [0, 1]
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- native-endian
|
||||
- "#address-cells"
|
||||
- "#size-cells"
|
||||
- ranges
|
||||
- dma-ranges
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
bus@50000000 {
|
||||
compatible = "intel,ixp42x-expansion-bus-controller", "syscon";
|
||||
reg = <0xc4000000 0x28>;
|
||||
native-endian;
|
||||
#address-cells = <2>;
|
||||
#size-cells = <1>;
|
||||
ranges = <0 0x0 0x50000000 0x01000000>,
|
||||
<1 0x0 0x51000000 0x01000000>;
|
||||
dma-ranges = <0 0x0 0x50000000 0x01000000>,
|
||||
<1 0x0 0x51000000 0x01000000>;
|
||||
flash@0,0 {
|
||||
compatible = "intel,ixp4xx-flash", "cfi-flash";
|
||||
bank-width = <2>;
|
||||
reg = <0 0x00000000 0x1000000>;
|
||||
intel,ixp4xx-eb-t3 = <3>;
|
||||
intel,ixp4xx-eb-cycle-type = <0>;
|
||||
intel,ixp4xx-eb-byte-access-on-halfword = <1>;
|
||||
intel,ixp4xx-eb-write-enable = <1>;
|
||||
intel,ixp4xx-eb-byte-access = <0>;
|
||||
};
|
||||
serial@1,0 {
|
||||
compatible = "exar,xr16l2551", "ns8250";
|
||||
reg = <1 0x00000000 0x10>;
|
||||
interrupt-parent = <&gpio0>;
|
||||
interrupts = <4 IRQ_TYPE_LEVEL_LOW>;
|
||||
clock-frequency = <1843200>;
|
||||
intel,ixp4xx-eb-t3 = <3>;
|
||||
intel,ixp4xx-eb-cycle-type = <1>;
|
||||
intel,ixp4xx-eb-write-enable = <1>;
|
||||
intel,ixp4xx-eb-byte-access = <1>;
|
||||
};
|
||||
};
|
|
@ -2010,10 +2010,12 @@ M: Krzysztof Halasa <khalasa@piap.pl>
|
|||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/arm/intel-ixp4xx.yaml
|
||||
F: Documentation/devicetree/bindings/bus/intel,ixp4xx-expansion-bus-controller.yaml
|
||||
F: Documentation/devicetree/bindings/gpio/intel,ixp4xx-gpio.txt
|
||||
F: Documentation/devicetree/bindings/interrupt-controller/intel,ixp4xx-interrupt.yaml
|
||||
F: Documentation/devicetree/bindings/timer/intel,ixp4xx-timer.yaml
|
||||
F: arch/arm/mach-ixp4xx/
|
||||
F: drivers/bus/intel-ixp4xx-eb.c
|
||||
F: drivers/clocksource/timer-ixp4xx.c
|
||||
F: drivers/crypto/ixp4xx_crypto.c
|
||||
F: drivers/gpio/gpio-ixp4xx.c
|
||||
|
|
|
@ -13,45 +13,134 @@
|
|||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/libata.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/platform_data/pata_ixp4xx_cf.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <scsi/scsi_host.h>
|
||||
|
||||
#define DRV_NAME "pata_ixp4xx_cf"
|
||||
#define DRV_VERSION "0.2"
|
||||
#define DRV_VERSION "1.0"
|
||||
|
||||
static int ixp4xx_set_mode(struct ata_link *link, struct ata_device **error)
|
||||
struct ixp4xx_pata {
|
||||
struct ata_host *host;
|
||||
struct regmap *rmap;
|
||||
u32 cmd_csreg;
|
||||
void __iomem *cmd;
|
||||
void __iomem *ctl;
|
||||
};
|
||||
|
||||
#define IXP4XX_EXP_TIMING_STRIDE 0x04
|
||||
/* The timings for the chipselect is in bits 29..16 */
|
||||
#define IXP4XX_EXP_T1_T5_MASK GENMASK(29, 16)
|
||||
#define IXP4XX_EXP_PIO_0_8 0x0a470000
|
||||
#define IXP4XX_EXP_PIO_1_8 0x06430000
|
||||
#define IXP4XX_EXP_PIO_2_8 0x02410000
|
||||
#define IXP4XX_EXP_PIO_3_8 0x00820000
|
||||
#define IXP4XX_EXP_PIO_4_8 0x00400000
|
||||
#define IXP4XX_EXP_PIO_0_16 0x29640000
|
||||
#define IXP4XX_EXP_PIO_1_16 0x05030000
|
||||
#define IXP4XX_EXP_PIO_2_16 0x00b20000
|
||||
#define IXP4XX_EXP_PIO_3_16 0x00820000
|
||||
#define IXP4XX_EXP_PIO_4_16 0x00400000
|
||||
#define IXP4XX_EXP_BW_MASK (BIT(6)|BIT(0))
|
||||
#define IXP4XX_EXP_BYTE_RD16 BIT(6) /* Byte reads on half-word devices */
|
||||
#define IXP4XX_EXP_BYTE_EN BIT(0) /* Use 8bit data bus if set */
|
||||
|
||||
static void ixp4xx_set_8bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode)
|
||||
{
|
||||
struct ata_device *dev;
|
||||
|
||||
ata_for_each_dev(dev, link, ENABLED) {
|
||||
ata_dev_info(dev, "configured for PIO0\n");
|
||||
dev->pio_mode = XFER_PIO_0;
|
||||
dev->xfer_mode = XFER_PIO_0;
|
||||
dev->xfer_shift = ATA_SHIFT_PIO;
|
||||
dev->flags |= ATA_DFLAG_PIO;
|
||||
switch (pio_mode) {
|
||||
case XFER_PIO_0:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_8);
|
||||
break;
|
||||
case XFER_PIO_1:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_8);
|
||||
break;
|
||||
case XFER_PIO_2:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_8);
|
||||
break;
|
||||
case XFER_PIO_3:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_8);
|
||||
break;
|
||||
case XFER_PIO_4:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_8);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16|IXP4XX_EXP_BYTE_EN);
|
||||
}
|
||||
|
||||
static void ixp4xx_set_16bit_timing(struct ixp4xx_pata *ixpp, u8 pio_mode)
|
||||
{
|
||||
switch (pio_mode){
|
||||
case XFER_PIO_0:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_0_16);
|
||||
break;
|
||||
case XFER_PIO_1:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_1_16);
|
||||
break;
|
||||
case XFER_PIO_2:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_2_16);
|
||||
break;
|
||||
case XFER_PIO_3:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_3_16);
|
||||
break;
|
||||
case XFER_PIO_4:
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_T1_T5_MASK, IXP4XX_EXP_PIO_4_16);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
regmap_update_bits(ixpp->rmap, ixpp->cmd_csreg,
|
||||
IXP4XX_EXP_BW_MASK, IXP4XX_EXP_BYTE_RD16);
|
||||
}
|
||||
|
||||
/* This sets up the timing on the chipselect CMD accordingly */
|
||||
static void ixp4xx_set_piomode(struct ata_port *ap, struct ata_device *adev)
|
||||
{
|
||||
struct ixp4xx_pata *ixpp = ap->host->private_data;
|
||||
|
||||
ata_dev_printk(adev, KERN_INFO, "configured for PIO%d 8bit\n",
|
||||
adev->pio_mode - XFER_PIO_0);
|
||||
ixp4xx_set_8bit_timing(ixpp, adev->pio_mode);
|
||||
}
|
||||
|
||||
|
||||
static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
|
||||
unsigned char *buf, unsigned int buflen, int rw)
|
||||
unsigned char *buf, unsigned int buflen, int rw)
|
||||
{
|
||||
unsigned int i;
|
||||
unsigned int words = buflen >> 1;
|
||||
u16 *buf16 = (u16 *) buf;
|
||||
struct ata_device *adev = qc->dev;
|
||||
struct ata_port *ap = qc->dev->link->ap;
|
||||
void __iomem *mmio = ap->ioaddr.data_addr;
|
||||
struct ixp4xx_pata_data *data = dev_get_platdata(ap->host->dev);
|
||||
struct ixp4xx_pata *ixpp = ap->host->private_data;
|
||||
unsigned long flags;
|
||||
|
||||
ata_dev_printk(adev, KERN_DEBUG, "%s %d bytes\n", (rw == READ) ? "READ" : "WRITE",
|
||||
buflen);
|
||||
spin_lock_irqsave(ap->lock, flags);
|
||||
|
||||
/* set the expansion bus in 16bit mode and restore
|
||||
* 8 bit mode after the transaction.
|
||||
*/
|
||||
*data->cs0_cfg &= ~(0x01);
|
||||
udelay(100);
|
||||
ixp4xx_set_16bit_timing(ixpp, adev->pio_mode);
|
||||
udelay(5);
|
||||
|
||||
/* Transfer multiple of 2 bytes */
|
||||
if (rw == READ)
|
||||
|
@ -76,8 +165,10 @@ static unsigned int ixp4xx_mmio_data_xfer(struct ata_queued_cmd *qc,
|
|||
words++;
|
||||
}
|
||||
|
||||
udelay(100);
|
||||
*data->cs0_cfg |= 0x01;
|
||||
ixp4xx_set_8bit_timing(ixpp, adev->pio_mode);
|
||||
udelay(5);
|
||||
|
||||
spin_unlock_irqrestore(ap->lock, flags);
|
||||
|
||||
return words << 1;
|
||||
}
|
||||
|
@ -90,79 +181,98 @@ static struct ata_port_operations ixp4xx_port_ops = {
|
|||
.inherits = &ata_sff_port_ops,
|
||||
.sff_data_xfer = ixp4xx_mmio_data_xfer,
|
||||
.cable_detect = ata_cable_40wire,
|
||||
.set_mode = ixp4xx_set_mode,
|
||||
.set_piomode = ixp4xx_set_piomode,
|
||||
};
|
||||
|
||||
static struct ata_port_info ixp4xx_port_info = {
|
||||
.flags = ATA_FLAG_NO_ATAPI,
|
||||
.pio_mask = ATA_PIO4,
|
||||
.port_ops = &ixp4xx_port_ops,
|
||||
};
|
||||
|
||||
static void ixp4xx_setup_port(struct ata_port *ap,
|
||||
struct ixp4xx_pata_data *data,
|
||||
unsigned long raw_cs0, unsigned long raw_cs1)
|
||||
struct ixp4xx_pata *ixpp,
|
||||
unsigned long raw_cmd, unsigned long raw_ctl)
|
||||
{
|
||||
struct ata_ioports *ioaddr = &ap->ioaddr;
|
||||
unsigned long raw_cmd = raw_cs0;
|
||||
unsigned long raw_ctl = raw_cs1 + 0x06;
|
||||
|
||||
ioaddr->cmd_addr = data->cs0;
|
||||
ioaddr->altstatus_addr = data->cs1 + 0x06;
|
||||
ioaddr->ctl_addr = data->cs1 + 0x06;
|
||||
raw_ctl += 0x06;
|
||||
ioaddr->cmd_addr = ixpp->cmd;
|
||||
ioaddr->altstatus_addr = ixpp->ctl + 0x06;
|
||||
ioaddr->ctl_addr = ixpp->ctl + 0x06;
|
||||
|
||||
ata_sff_std_ports(ioaddr);
|
||||
|
||||
#ifndef __ARMEB__
|
||||
if (!IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) {
|
||||
/* adjust the addresses to handle the address swizzling of the
|
||||
* ixp4xx in little endian mode.
|
||||
*/
|
||||
|
||||
/* adjust the addresses to handle the address swizzling of the
|
||||
* ixp4xx in little endian mode.
|
||||
*/
|
||||
*(unsigned long *)&ioaddr->data_addr ^= 0x02;
|
||||
*(unsigned long *)&ioaddr->cmd_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->altstatus_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->ctl_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->error_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->feature_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->nsect_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->lbal_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->lbam_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->lbah_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->device_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->status_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->command_addr ^= 0x03;
|
||||
|
||||
*(unsigned long *)&ioaddr->data_addr ^= 0x02;
|
||||
*(unsigned long *)&ioaddr->cmd_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->altstatus_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->ctl_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->error_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->feature_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->nsect_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->lbal_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->lbam_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->lbah_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->device_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->status_addr ^= 0x03;
|
||||
*(unsigned long *)&ioaddr->command_addr ^= 0x03;
|
||||
|
||||
raw_cmd ^= 0x03;
|
||||
raw_ctl ^= 0x03;
|
||||
#endif
|
||||
raw_cmd ^= 0x03;
|
||||
raw_ctl ^= 0x03;
|
||||
}
|
||||
|
||||
ata_port_desc(ap, "cmd 0x%lx ctl 0x%lx", raw_cmd, raw_ctl);
|
||||
}
|
||||
|
||||
static int ixp4xx_pata_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *cs0, *cs1;
|
||||
struct ata_host *host;
|
||||
struct ata_port *ap;
|
||||
struct ixp4xx_pata_data *data = dev_get_platdata(&pdev->dev);
|
||||
struct resource *cmd, *ctl;
|
||||
struct ata_port_info pi = ixp4xx_port_info;
|
||||
const struct ata_port_info *ppi[] = { &pi, NULL };
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct ixp4xx_pata *ixpp;
|
||||
u32 csindex;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
cs0 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
cs1 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
cmd = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
ctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
|
||||
|
||||
if (!cs0 || !cs1)
|
||||
if (!cmd || !ctl)
|
||||
return -EINVAL;
|
||||
|
||||
/* allocate host */
|
||||
host = ata_host_alloc(&pdev->dev, 1);
|
||||
if (!host)
|
||||
ixpp = devm_kzalloc(dev, sizeof(*ixpp), GFP_KERNEL);
|
||||
if (!ixpp)
|
||||
return -ENOMEM;
|
||||
|
||||
/* acquire resources and fill host */
|
||||
ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
|
||||
ixpp->rmap = syscon_node_to_regmap(np->parent);
|
||||
if (IS_ERR(ixpp->rmap))
|
||||
return dev_err_probe(dev, PTR_ERR(ixpp->rmap), "no regmap\n");
|
||||
/* Inspect our address to figure out what chipselect the CMD is on */
|
||||
ret = of_property_read_u32_index(np, "reg", 0, &csindex);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "can't inspect CMD address\n");
|
||||
dev_info(dev, "using CS%d for PIO timing configuration\n", csindex);
|
||||
ixpp->cmd_csreg = csindex * IXP4XX_EXP_TIMING_STRIDE;
|
||||
|
||||
ixpp->host = ata_host_alloc_pinfo(dev, ppi, 1);
|
||||
if (!ixpp->host)
|
||||
return -ENOMEM;
|
||||
ixpp->host->private_data = ixpp;
|
||||
|
||||
ret = dma_set_coherent_mask(dev, DMA_BIT_MASK(32));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
data->cs0 = devm_ioremap(&pdev->dev, cs0->start, 0x1000);
|
||||
data->cs1 = devm_ioremap(&pdev->dev, cs1->start, 0x1000);
|
||||
|
||||
if (!data->cs0 || !data->cs1)
|
||||
ixpp->cmd = devm_ioremap_resource(dev, cmd);
|
||||
ixpp->ctl = devm_ioremap_resource(dev, ctl);
|
||||
if (IS_ERR(ixpp->cmd) || IS_ERR(ixpp->ctl))
|
||||
return -ENOMEM;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
@ -173,27 +283,23 @@ static int ixp4xx_pata_probe(struct platform_device *pdev)
|
|||
else
|
||||
return -EINVAL;
|
||||
|
||||
/* Setup expansion bus chip selects */
|
||||
*data->cs0_cfg = data->cs0_bits;
|
||||
*data->cs1_cfg = data->cs1_bits;
|
||||
/* Just one port to set up */
|
||||
ixp4xx_setup_port(ixpp->host->ports[0], ixpp, cmd->start, ctl->start);
|
||||
|
||||
ap = host->ports[0];
|
||||
ata_print_version_once(dev, DRV_VERSION);
|
||||
|
||||
ap->ops = &ixp4xx_port_ops;
|
||||
ap->pio_mask = ATA_PIO4;
|
||||
ap->flags |= ATA_FLAG_NO_ATAPI;
|
||||
|
||||
ixp4xx_setup_port(ap, data, cs0->start, cs1->start);
|
||||
|
||||
ata_print_version_once(&pdev->dev, DRV_VERSION);
|
||||
|
||||
/* activate host */
|
||||
return ata_host_activate(host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
|
||||
return ata_host_activate(ixpp->host, irq, ata_sff_interrupt, 0, &ixp4xx_sht);
|
||||
}
|
||||
|
||||
static const struct of_device_id ixp4xx_pata_of_match[] = {
|
||||
{ .compatible = "intel,ixp4xx-compact-flash", },
|
||||
{ },
|
||||
};
|
||||
|
||||
static struct platform_driver ixp4xx_pata_platform_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.of_match_table = ixp4xx_pata_of_match,
|
||||
},
|
||||
.probe = ixp4xx_pata_probe,
|
||||
.remove = ata_platform_remove_one,
|
||||
|
|
|
@ -95,6 +95,17 @@ config IMX_WEIM
|
|||
The WEIM(Wireless External Interface Module) works like a bus.
|
||||
You can attach many different devices on it, such as NOR, onenand.
|
||||
|
||||
config INTEL_IXP4XX_EB
|
||||
bool "Intel IXP4xx expansion bus interface driver"
|
||||
depends on HAS_IOMEM
|
||||
depends on ARCH_IXP4XX || COMPILE_TEST
|
||||
default ARCH_IXP4XX
|
||||
select MFD_SYSCON
|
||||
help
|
||||
Driver for the Intel IXP4xx expansion bus interface. The driver is
|
||||
needed to set up various chip select configuration parameters before
|
||||
devices on the expansion bus can be discovered.
|
||||
|
||||
config MIPS_CDMM
|
||||
bool "MIPS Common Device Memory Map (CDMM) Driver"
|
||||
depends on CPU_MIPSR2 || CPU_MIPSR5
|
||||
|
|
|
@ -16,6 +16,7 @@ obj-$(CONFIG_FSL_MC_BUS) += fsl-mc/
|
|||
obj-$(CONFIG_BT1_APB) += bt1-apb.o
|
||||
obj-$(CONFIG_BT1_AXI) += bt1-axi.o
|
||||
obj-$(CONFIG_IMX_WEIM) += imx-weim.o
|
||||
obj-$(CONFIG_INTEL_IXP4XX_EB) += intel-ixp4xx-eb.o
|
||||
obj-$(CONFIG_MIPS_CDMM) += mips_cdmm.o
|
||||
obj-$(CONFIG_MVEBU_MBUS) += mvebu-mbus.o
|
||||
|
||||
|
|
429
drivers/bus/intel-ixp4xx-eb.c
Normal file
429
drivers/bus/intel-ixp4xx-eb.c
Normal file
|
@ -0,0 +1,429 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* Intel IXP4xx Expansion Bus Controller
|
||||
* Copyright (C) 2021 Linaro Ltd.
|
||||
*
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*/
|
||||
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/log2.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
#define IXP4XX_EXP_NUM_CS 8
|
||||
|
||||
#define IXP4XX_EXP_TIMING_CS0 0x00
|
||||
#define IXP4XX_EXP_TIMING_CS1 0x04
|
||||
#define IXP4XX_EXP_TIMING_CS2 0x08
|
||||
#define IXP4XX_EXP_TIMING_CS3 0x0c
|
||||
#define IXP4XX_EXP_TIMING_CS4 0x10
|
||||
#define IXP4XX_EXP_TIMING_CS5 0x14
|
||||
#define IXP4XX_EXP_TIMING_CS6 0x18
|
||||
#define IXP4XX_EXP_TIMING_CS7 0x1c
|
||||
|
||||
/* Bits inside each CS timing register */
|
||||
#define IXP4XX_EXP_TIMING_STRIDE 0x04
|
||||
#define IXP4XX_EXP_CS_EN BIT(31)
|
||||
#define IXP456_EXP_PAR_EN BIT(30) /* Only on IXP45x and IXP46x */
|
||||
#define IXP4XX_EXP_T1_MASK GENMASK(28, 27)
|
||||
#define IXP4XX_EXP_T1_SHIFT 28
|
||||
#define IXP4XX_EXP_T2_MASK GENMASK(27, 26)
|
||||
#define IXP4XX_EXP_T2_SHIFT 26
|
||||
#define IXP4XX_EXP_T3_MASK GENMASK(25, 22)
|
||||
#define IXP4XX_EXP_T3_SHIFT 22
|
||||
#define IXP4XX_EXP_T4_MASK GENMASK(21, 20)
|
||||
#define IXP4XX_EXP_T4_SHIFT 20
|
||||
#define IXP4XX_EXP_T5_MASK GENMASK(19, 16)
|
||||
#define IXP4XX_EXP_T5_SHIFT 16
|
||||
#define IXP4XX_EXP_CYC_TYPE_MASK GENMASK(15, 14)
|
||||
#define IXP4XX_EXP_CYC_TYPE_SHIFT 14
|
||||
#define IXP4XX_EXP_SIZE_MASK GENMASK(13, 10)
|
||||
#define IXP4XX_EXP_SIZE_SHIFT 10
|
||||
#define IXP4XX_EXP_CNFG_0 BIT(9) /* Always zero */
|
||||
#define IXP43X_EXP_SYNC_INTEL BIT(8) /* Only on IXP43x */
|
||||
#define IXP43X_EXP_EXP_CHIP BIT(7) /* Only on IXP43x */
|
||||
#define IXP4XX_EXP_BYTE_RD16 BIT(6)
|
||||
#define IXP4XX_EXP_HRDY_POL BIT(5) /* Only on IXP42x */
|
||||
#define IXP4XX_EXP_MUX_EN BIT(4)
|
||||
#define IXP4XX_EXP_SPLT_EN BIT(3)
|
||||
#define IXP4XX_EXP_WORD BIT(2) /* Always zero */
|
||||
#define IXP4XX_EXP_WR_EN BIT(1)
|
||||
#define IXP4XX_EXP_BYTE_EN BIT(0)
|
||||
#define IXP42X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(8)|BIT(7)|IXP4XX_EXP_WORD)
|
||||
#define IXP43X_RESERVED (BIT(30)|IXP4XX_EXP_CNFG_0|BIT(5)|IXP4XX_EXP_WORD)
|
||||
|
||||
#define IXP4XX_EXP_CNFG0 0x20
|
||||
#define IXP4XX_EXP_CNFG0_MEM_MAP BIT(31)
|
||||
#define IXP4XX_EXP_CNFG1 0x24
|
||||
|
||||
#define IXP4XX_EXP_BOOT_BASE 0x00000000
|
||||
#define IXP4XX_EXP_NORMAL_BASE 0x50000000
|
||||
#define IXP4XX_EXP_STRIDE 0x01000000
|
||||
|
||||
/* Fuses on the IXP43x */
|
||||
#define IXP43X_EXP_UNIT_FUSE_RESET 0x28
|
||||
#define IXP43x_EXP_FUSE_SPEED_MASK GENMASK(23, 22)
|
||||
|
||||
/* Number of device tree values in "reg" */
|
||||
#define IXP4XX_OF_REG_SIZE 3
|
||||
|
||||
struct ixp4xx_eb {
|
||||
struct device *dev;
|
||||
struct regmap *rmap;
|
||||
u32 bus_base;
|
||||
bool is_42x;
|
||||
bool is_43x;
|
||||
};
|
||||
|
||||
struct ixp4xx_exp_tim_prop {
|
||||
const char *prop;
|
||||
u32 max;
|
||||
u32 mask;
|
||||
u16 shift;
|
||||
};
|
||||
|
||||
static const struct ixp4xx_exp_tim_prop ixp4xx_exp_tim_props[] = {
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-t1",
|
||||
.max = 3,
|
||||
.mask = IXP4XX_EXP_T1_MASK,
|
||||
.shift = IXP4XX_EXP_T1_SHIFT,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-t2",
|
||||
.max = 3,
|
||||
.mask = IXP4XX_EXP_T2_MASK,
|
||||
.shift = IXP4XX_EXP_T2_SHIFT,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-t3",
|
||||
.max = 15,
|
||||
.mask = IXP4XX_EXP_T3_MASK,
|
||||
.shift = IXP4XX_EXP_T3_SHIFT,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-t4",
|
||||
.max = 3,
|
||||
.mask = IXP4XX_EXP_T4_MASK,
|
||||
.shift = IXP4XX_EXP_T4_SHIFT,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-t5",
|
||||
.max = 15,
|
||||
.mask = IXP4XX_EXP_T5_MASK,
|
||||
.shift = IXP4XX_EXP_T5_SHIFT,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-byte-access-on-halfword",
|
||||
.max = 1,
|
||||
.mask = IXP4XX_EXP_BYTE_RD16,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-hpi-hrdy-pol-high",
|
||||
.max = 1,
|
||||
.mask = IXP4XX_EXP_HRDY_POL,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-mux-address-and-data",
|
||||
.max = 1,
|
||||
.mask = IXP4XX_EXP_MUX_EN,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-ahb-split-transfers",
|
||||
.max = 1,
|
||||
.mask = IXP4XX_EXP_SPLT_EN,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-write-enable",
|
||||
.max = 1,
|
||||
.mask = IXP4XX_EXP_WR_EN,
|
||||
},
|
||||
{
|
||||
.prop = "intel,ixp4xx-eb-byte-access",
|
||||
.max = 1,
|
||||
.mask = IXP4XX_EXP_BYTE_EN,
|
||||
},
|
||||
};
|
||||
|
||||
static void ixp4xx_exp_setup_chipselect(struct ixp4xx_eb *eb,
|
||||
struct device_node *np,
|
||||
u32 cs_index,
|
||||
u32 cs_size)
|
||||
{
|
||||
u32 cs_cfg;
|
||||
u32 val;
|
||||
u32 cur_cssize;
|
||||
u32 cs_order;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
if (eb->is_42x && (cs_index > 7)) {
|
||||
dev_err(eb->dev,
|
||||
"invalid chipselect %u, we only support 0-7\n",
|
||||
cs_index);
|
||||
return;
|
||||
}
|
||||
if (eb->is_43x && (cs_index > 3)) {
|
||||
dev_err(eb->dev,
|
||||
"invalid chipselect %u, we only support 0-3\n",
|
||||
cs_index);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Several chip selects can be joined into one device */
|
||||
if (cs_size > IXP4XX_EXP_STRIDE)
|
||||
cur_cssize = IXP4XX_EXP_STRIDE;
|
||||
else
|
||||
cur_cssize = cs_size;
|
||||
|
||||
|
||||
/*
|
||||
* The following will read/modify/write the configuration for one
|
||||
* chipselect, attempting to leave the boot defaults in place unless
|
||||
* something is explicitly defined.
|
||||
*/
|
||||
regmap_read(eb->rmap, IXP4XX_EXP_TIMING_CS0 +
|
||||
IXP4XX_EXP_TIMING_STRIDE * cs_index, &cs_cfg);
|
||||
dev_info(eb->dev, "CS%d at %#08x, size %#08x, config before: %#08x\n",
|
||||
cs_index, eb->bus_base + IXP4XX_EXP_STRIDE * cs_index,
|
||||
cur_cssize, cs_cfg);
|
||||
|
||||
/* Size set-up first align to 2^9 .. 2^24 */
|
||||
cur_cssize = roundup_pow_of_two(cur_cssize);
|
||||
if (cur_cssize < 512)
|
||||
cur_cssize = 512;
|
||||
cs_order = ilog2(cur_cssize);
|
||||
if (cs_order < 9 || cs_order > 24) {
|
||||
dev_err(eb->dev, "illegal size order %d\n", cs_order);
|
||||
return;
|
||||
}
|
||||
dev_dbg(eb->dev, "CS%d size order: %d\n", cs_index, cs_order);
|
||||
cs_cfg &= ~(IXP4XX_EXP_SIZE_MASK);
|
||||
cs_cfg |= ((cs_order - 9) << IXP4XX_EXP_SIZE_SHIFT);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ixp4xx_exp_tim_props); i++) {
|
||||
const struct ixp4xx_exp_tim_prop *ip = &ixp4xx_exp_tim_props[i];
|
||||
|
||||
/* All are regular u32 values */
|
||||
ret = of_property_read_u32(np, ip->prop, &val);
|
||||
if (ret)
|
||||
continue;
|
||||
|
||||
/* Handle bools (single bits) first */
|
||||
if (ip->max == 1) {
|
||||
if (val)
|
||||
cs_cfg |= ip->mask;
|
||||
else
|
||||
cs_cfg &= ~ip->mask;
|
||||
dev_info(eb->dev, "CS%d %s %s\n", cs_index,
|
||||
val ? "enabled" : "disabled",
|
||||
ip->prop);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (val > ip->max) {
|
||||
dev_err(eb->dev,
|
||||
"CS%d too high value for %s: %u, capped at %u\n",
|
||||
cs_index, ip->prop, val, ip->max);
|
||||
val = ip->max;
|
||||
}
|
||||
/* This assumes max value fills all the assigned bits (and it does) */
|
||||
cs_cfg &= ~ip->mask;
|
||||
cs_cfg |= (val << ip->shift);
|
||||
dev_info(eb->dev, "CS%d set %s to %u\n", cs_index, ip->prop, val);
|
||||
}
|
||||
|
||||
ret = of_property_read_u32(np, "intel,ixp4xx-eb-cycle-type", &val);
|
||||
if (!ret) {
|
||||
if (val > 3) {
|
||||
dev_err(eb->dev, "illegal cycle type %d\n", val);
|
||||
return;
|
||||
}
|
||||
dev_info(eb->dev, "CS%d set cycle type %d\n", cs_index, val);
|
||||
cs_cfg &= ~IXP4XX_EXP_CYC_TYPE_MASK;
|
||||
cs_cfg |= val << IXP4XX_EXP_CYC_TYPE_SHIFT;
|
||||
}
|
||||
|
||||
if (eb->is_42x)
|
||||
cs_cfg &= ~IXP42X_RESERVED;
|
||||
if (eb->is_43x) {
|
||||
cs_cfg &= ~IXP43X_RESERVED;
|
||||
/*
|
||||
* This bit for Intel strata flash is currently unused, but let's
|
||||
* report it if we find one.
|
||||
*/
|
||||
if (cs_cfg & IXP43X_EXP_SYNC_INTEL)
|
||||
dev_info(eb->dev, "claims to be Intel strata flash\n");
|
||||
}
|
||||
cs_cfg |= IXP4XX_EXP_CS_EN;
|
||||
|
||||
regmap_write(eb->rmap,
|
||||
IXP4XX_EXP_TIMING_CS0 + IXP4XX_EXP_TIMING_STRIDE * cs_index,
|
||||
cs_cfg);
|
||||
dev_info(eb->dev, "CS%d wrote %#08x into CS config\n", cs_index, cs_cfg);
|
||||
|
||||
/*
|
||||
* If several chip selects are joined together into one big
|
||||
* device area, we call ourselves recursively for each successive
|
||||
* chip select. For a 32MB flash chip this results in two calls
|
||||
* for example.
|
||||
*/
|
||||
if (cs_size > IXP4XX_EXP_STRIDE)
|
||||
ixp4xx_exp_setup_chipselect(eb, np,
|
||||
cs_index + 1,
|
||||
cs_size - IXP4XX_EXP_STRIDE);
|
||||
}
|
||||
|
||||
static void ixp4xx_exp_setup_child(struct ixp4xx_eb *eb,
|
||||
struct device_node *np)
|
||||
{
|
||||
u32 cs_sizes[IXP4XX_EXP_NUM_CS];
|
||||
int num_regs;
|
||||
u32 csindex;
|
||||
u32 cssize;
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
num_regs = of_property_count_elems_of_size(np, "reg", IXP4XX_OF_REG_SIZE);
|
||||
if (num_regs <= 0)
|
||||
return;
|
||||
dev_dbg(eb->dev, "child %s has %d register sets\n",
|
||||
of_node_full_name(np), num_regs);
|
||||
|
||||
for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++)
|
||||
cs_sizes[csindex] = 0;
|
||||
|
||||
for (i = 0; i < num_regs; i++) {
|
||||
u32 rbase, rsize;
|
||||
|
||||
ret = of_property_read_u32_index(np, "reg",
|
||||
i * IXP4XX_OF_REG_SIZE, &csindex);
|
||||
if (ret)
|
||||
break;
|
||||
ret = of_property_read_u32_index(np, "reg",
|
||||
i * IXP4XX_OF_REG_SIZE + 1, &rbase);
|
||||
if (ret)
|
||||
break;
|
||||
ret = of_property_read_u32_index(np, "reg",
|
||||
i * IXP4XX_OF_REG_SIZE + 2, &rsize);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (csindex >= IXP4XX_EXP_NUM_CS) {
|
||||
dev_err(eb->dev, "illegal CS %d\n", csindex);
|
||||
continue;
|
||||
}
|
||||
/*
|
||||
* The memory window always starts from CS base so we need to add
|
||||
* the start and size to get to the size from the start of the CS
|
||||
* base. For example if CS0 is at 0x50000000 and the reg is
|
||||
* <0 0xe40000 0x40000> the size is e80000.
|
||||
*
|
||||
* Roof this if we have several regs setting the same CS.
|
||||
*/
|
||||
cssize = rbase + rsize;
|
||||
dev_dbg(eb->dev, "CS%d size %#08x\n", csindex, cssize);
|
||||
if (cs_sizes[csindex] < cssize)
|
||||
cs_sizes[csindex] = cssize;
|
||||
}
|
||||
|
||||
for (csindex = 0; csindex < IXP4XX_EXP_NUM_CS; csindex++) {
|
||||
cssize = cs_sizes[csindex];
|
||||
if (!cssize)
|
||||
continue;
|
||||
/* Just this one, so set it up and return */
|
||||
ixp4xx_exp_setup_chipselect(eb, np, csindex, cssize);
|
||||
}
|
||||
}
|
||||
|
||||
static int ixp4xx_exp_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct device_node *np = dev->of_node;
|
||||
struct ixp4xx_eb *eb;
|
||||
struct device_node *child;
|
||||
bool have_children = false;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
eb = devm_kzalloc(dev, sizeof(*eb), GFP_KERNEL);
|
||||
if (!eb)
|
||||
return -ENOMEM;
|
||||
|
||||
eb->dev = dev;
|
||||
eb->is_42x = of_device_is_compatible(np, "intel,ixp42x-expansion-bus-controller");
|
||||
eb->is_43x = of_device_is_compatible(np, "intel,ixp43x-expansion-bus-controller");
|
||||
|
||||
eb->rmap = syscon_node_to_regmap(np);
|
||||
if (IS_ERR(eb->rmap))
|
||||
return dev_err_probe(dev, PTR_ERR(eb->rmap), "no regmap\n");
|
||||
|
||||
/* We check that the regmap work only on first read */
|
||||
ret = regmap_read(eb->rmap, IXP4XX_EXP_CNFG0, &val);
|
||||
if (ret)
|
||||
dev_err_probe(dev, ret, "cannot read regmap\n");
|
||||
if (val & IXP4XX_EXP_CNFG0_MEM_MAP)
|
||||
eb->bus_base = IXP4XX_EXP_BOOT_BASE;
|
||||
else
|
||||
eb->bus_base = IXP4XX_EXP_NORMAL_BASE;
|
||||
dev_info(dev, "expansion bus at %08x\n", eb->bus_base);
|
||||
|
||||
if (eb->is_43x) {
|
||||
/* Check some fuses */
|
||||
regmap_read(eb->rmap, IXP43X_EXP_UNIT_FUSE_RESET, &val);
|
||||
switch (FIELD_GET(IXP43x_EXP_FUSE_SPEED_MASK, val)) {
|
||||
case 0:
|
||||
dev_info(dev, "IXP43x at 533 MHz\n");
|
||||
break;
|
||||
case 1:
|
||||
dev_info(dev, "IXP43x at 400 MHz\n");
|
||||
break;
|
||||
case 2:
|
||||
dev_info(dev, "IXP43x at 667 MHz\n");
|
||||
break;
|
||||
default:
|
||||
dev_info(dev, "IXP43x unknown speed\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Walk over the child nodes and see what chipselects we use */
|
||||
for_each_available_child_of_node(np, child) {
|
||||
ixp4xx_exp_setup_child(eb, child);
|
||||
/* We have at least one child */
|
||||
have_children = true;
|
||||
}
|
||||
|
||||
if (have_children)
|
||||
return of_platform_default_populate(np, NULL, dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id ixp4xx_exp_of_match[] = {
|
||||
{ .compatible = "intel,ixp42x-expansion-bus-controller", },
|
||||
{ .compatible = "intel,ixp43x-expansion-bus-controller", },
|
||||
{ .compatible = "intel,ixp45x-expansion-bus-controller", },
|
||||
{ .compatible = "intel,ixp46x-expansion-bus-controller", },
|
||||
{ }
|
||||
};
|
||||
|
||||
static struct platform_driver ixp4xx_exp_driver = {
|
||||
.probe = ixp4xx_exp_probe,
|
||||
.driver = {
|
||||
.name = "intel-extbus",
|
||||
.of_match_table = ixp4xx_exp_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(ixp4xx_exp_driver);
|
||||
MODULE_AUTHOR("Linus Walleij <linus.walleij@linaro.org>");
|
||||
MODULE_DESCRIPTION("Intel IXP4xx external bus driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/platform_device.h>
|
||||
/* Goes away with OF conversion */
|
||||
#include <linux/platform_data/timer-ixp4xx.h>
|
||||
|
||||
|
@ -29,9 +30,6 @@
|
|||
#define IXP4XX_OSRT1_OFFSET 0x08 /* Timer 1 Reload */
|
||||
#define IXP4XX_OST2_OFFSET 0x0C /* Timer 2 Timestamp */
|
||||
#define IXP4XX_OSRT2_OFFSET 0x10 /* Timer 2 Reload */
|
||||
#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */
|
||||
#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */
|
||||
#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */
|
||||
#define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */
|
||||
|
||||
/*
|
||||
|
@ -45,17 +43,10 @@
|
|||
#define IXP4XX_OSST_TIMER_1_PEND 0x00000001
|
||||
#define IXP4XX_OSST_TIMER_2_PEND 0x00000002
|
||||
#define IXP4XX_OSST_TIMER_TS_PEND 0x00000004
|
||||
#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008
|
||||
#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010
|
||||
|
||||
#define IXP4XX_WDT_KEY 0x0000482E
|
||||
#define IXP4XX_WDT_RESET_ENABLE 0x00000001
|
||||
#define IXP4XX_WDT_IRQ_ENABLE 0x00000002
|
||||
#define IXP4XX_WDT_COUNT_ENABLE 0x00000004
|
||||
/* Remaining registers are for the watchdog and defined in the watchdog driver */
|
||||
|
||||
struct ixp4xx_timer {
|
||||
void __iomem *base;
|
||||
unsigned int tick_rate;
|
||||
u32 latch;
|
||||
struct clock_event_device clkevt;
|
||||
#ifdef CONFIG_ARM
|
||||
|
@ -181,7 +172,6 @@ static __init int ixp4xx_timer_register(void __iomem *base,
|
|||
if (!tmr)
|
||||
return -ENOMEM;
|
||||
tmr->base = base;
|
||||
tmr->tick_rate = timer_freq;
|
||||
|
||||
/*
|
||||
* The timer register doesn't allow to specify the two least
|
||||
|
@ -239,6 +229,40 @@ static __init int ixp4xx_timer_register(void __iomem *base,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_device ixp4xx_watchdog_device = {
|
||||
.name = "ixp4xx-watchdog",
|
||||
.id = -1,
|
||||
};
|
||||
|
||||
/*
|
||||
* This probe gets called after the timer is already up and running. The main
|
||||
* function on this platform is to spawn the watchdog device as a child.
|
||||
*/
|
||||
static int ixp4xx_timer_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
||||
/* Pass the base address as platform data and nothing else */
|
||||
ixp4xx_watchdog_device.dev.platform_data = local_ixp4xx_timer->base;
|
||||
ixp4xx_watchdog_device.dev.parent = dev;
|
||||
return platform_device_register(&ixp4xx_watchdog_device);
|
||||
}
|
||||
|
||||
static const struct of_device_id ixp4xx_timer_dt_id[] = {
|
||||
{ .compatible = "intel,ixp4xx-timer", },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
|
||||
static struct platform_driver ixp4xx_timer_driver = {
|
||||
.probe = ixp4xx_timer_probe,
|
||||
.driver = {
|
||||
.name = "ixp4xx-timer",
|
||||
.of_match_table = ixp4xx_timer_dt_id,
|
||||
.suppress_bind_attrs = true,
|
||||
},
|
||||
};
|
||||
builtin_platform_driver(ixp4xx_timer_driver);
|
||||
|
||||
/**
|
||||
* ixp4xx_timer_setup() - Timer setup function to be called from boardfiles
|
||||
* @timerbase: physical base of timer block
|
||||
|
|
|
@ -487,6 +487,7 @@ config FTWDT010_WATCHDOG
|
|||
config IXP4XX_WATCHDOG
|
||||
tristate "IXP4xx Watchdog"
|
||||
depends on ARCH_IXP4XX
|
||||
select WATCHDOG_CORE
|
||||
help
|
||||
Say Y here if to include support for the watchdog timer
|
||||
in the Intel IXP4xx network processors. This driver can
|
||||
|
|
|
@ -1,220 +1,173 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/*
|
||||
* drivers/char/watchdog/ixp4xx_wdt.c
|
||||
*
|
||||
* Watchdog driver for Intel IXP4xx network processors
|
||||
*
|
||||
* Author: Deepak Saxena <dsaxena@plexity.net>
|
||||
* Author: Linus Walleij <linus.walleij@linaro.org>
|
||||
*
|
||||
* Copyright 2004 (c) MontaVista, Software, Inc.
|
||||
* Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
|
||||
*
|
||||
* This file is licensed under the terms of the GNU General Public
|
||||
* License version 2. This program is licensed "as is" without any
|
||||
* warranty of any kind, whether express or implied.
|
||||
*/
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/watchdog.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <mach/hardware.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/clk.h>
|
||||
#include <linux/soc/ixp4xx/cpu.h>
|
||||
|
||||
static bool nowayout = WATCHDOG_NOWAYOUT;
|
||||
static int heartbeat = 60; /* (secs) Default is 1 minute */
|
||||
static unsigned long wdt_status;
|
||||
static unsigned long boot_status;
|
||||
static DEFINE_SPINLOCK(wdt_lock);
|
||||
|
||||
#define WDT_TICK_RATE (IXP4XX_PERIPHERAL_BUS_CLOCK * 1000000UL)
|
||||
|
||||
#define WDT_IN_USE 0
|
||||
#define WDT_OK_TO_CLOSE 1
|
||||
|
||||
static void wdt_enable(void)
|
||||
{
|
||||
spin_lock(&wdt_lock);
|
||||
*IXP4XX_OSWK = IXP4XX_WDT_KEY;
|
||||
*IXP4XX_OSWE = 0;
|
||||
*IXP4XX_OSWT = WDT_TICK_RATE * heartbeat;
|
||||
*IXP4XX_OSWE = IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE;
|
||||
*IXP4XX_OSWK = 0;
|
||||
spin_unlock(&wdt_lock);
|
||||
}
|
||||
|
||||
static void wdt_disable(void)
|
||||
{
|
||||
spin_lock(&wdt_lock);
|
||||
*IXP4XX_OSWK = IXP4XX_WDT_KEY;
|
||||
*IXP4XX_OSWE = 0;
|
||||
*IXP4XX_OSWK = 0;
|
||||
spin_unlock(&wdt_lock);
|
||||
}
|
||||
|
||||
static int ixp4xx_wdt_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
if (test_and_set_bit(WDT_IN_USE, &wdt_status))
|
||||
return -EBUSY;
|
||||
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
wdt_enable();
|
||||
return stream_open(inode, file);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ixp4xx_wdt_write(struct file *file, const char *data, size_t len, loff_t *ppos)
|
||||
{
|
||||
if (len) {
|
||||
if (!nowayout) {
|
||||
size_t i;
|
||||
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
|
||||
for (i = 0; i != len; i++) {
|
||||
char c;
|
||||
|
||||
if (get_user(c, data + i))
|
||||
return -EFAULT;
|
||||
if (c == 'V')
|
||||
set_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
}
|
||||
}
|
||||
wdt_enable();
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static const struct watchdog_info ident = {
|
||||
.options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
|
||||
WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
|
||||
.identity = "IXP4xx Watchdog",
|
||||
struct ixp4xx_wdt {
|
||||
struct watchdog_device wdd;
|
||||
void __iomem *base;
|
||||
unsigned long rate;
|
||||
};
|
||||
|
||||
/* Fallback if we do not have a clock for this */
|
||||
#define IXP4XX_TIMER_FREQ 66666000
|
||||
|
||||
static long ixp4xx_wdt_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
/* Registers after the timer registers */
|
||||
#define IXP4XX_OSWT_OFFSET 0x14 /* Watchdog Timer */
|
||||
#define IXP4XX_OSWE_OFFSET 0x18 /* Watchdog Enable */
|
||||
#define IXP4XX_OSWK_OFFSET 0x1C /* Watchdog Key */
|
||||
#define IXP4XX_OSST_OFFSET 0x20 /* Timer Status */
|
||||
|
||||
#define IXP4XX_OSST_TIMER_WDOG_PEND 0x00000008
|
||||
#define IXP4XX_OSST_TIMER_WARM_RESET 0x00000010
|
||||
#define IXP4XX_WDT_KEY 0x0000482E
|
||||
#define IXP4XX_WDT_RESET_ENABLE 0x00000001
|
||||
#define IXP4XX_WDT_IRQ_ENABLE 0x00000002
|
||||
#define IXP4XX_WDT_COUNT_ENABLE 0x00000004
|
||||
|
||||
static inline
|
||||
struct ixp4xx_wdt *to_ixp4xx_wdt(struct watchdog_device *wdd)
|
||||
{
|
||||
int ret = -ENOTTY;
|
||||
int time;
|
||||
|
||||
switch (cmd) {
|
||||
case WDIOC_GETSUPPORT:
|
||||
ret = copy_to_user((struct watchdog_info *)arg, &ident,
|
||||
sizeof(ident)) ? -EFAULT : 0;
|
||||
break;
|
||||
|
||||
case WDIOC_GETSTATUS:
|
||||
ret = put_user(0, (int *)arg);
|
||||
break;
|
||||
|
||||
case WDIOC_GETBOOTSTATUS:
|
||||
ret = put_user(boot_status, (int *)arg);
|
||||
break;
|
||||
|
||||
case WDIOC_KEEPALIVE:
|
||||
wdt_enable();
|
||||
ret = 0;
|
||||
break;
|
||||
|
||||
case WDIOC_SETTIMEOUT:
|
||||
ret = get_user(time, (int *)arg);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (time <= 0 || time > 60) {
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
heartbeat = time;
|
||||
wdt_enable();
|
||||
fallthrough;
|
||||
|
||||
case WDIOC_GETTIMEOUT:
|
||||
ret = put_user(heartbeat, (int *)arg);
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
return container_of(wdd, struct ixp4xx_wdt, wdd);
|
||||
}
|
||||
|
||||
static int ixp4xx_wdt_release(struct inode *inode, struct file *file)
|
||||
static int ixp4xx_wdt_start(struct watchdog_device *wdd)
|
||||
{
|
||||
if (test_bit(WDT_OK_TO_CLOSE, &wdt_status))
|
||||
wdt_disable();
|
||||
else
|
||||
pr_crit("Device closed unexpectedly - timer will not stop\n");
|
||||
clear_bit(WDT_IN_USE, &wdt_status);
|
||||
clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
|
||||
struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd);
|
||||
|
||||
__raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET);
|
||||
__raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET);
|
||||
__raw_writel(wdd->timeout * iwdt->rate,
|
||||
iwdt->base + IXP4XX_OSWT_OFFSET);
|
||||
__raw_writel(IXP4XX_WDT_COUNT_ENABLE | IXP4XX_WDT_RESET_ENABLE,
|
||||
iwdt->base + IXP4XX_OSWE_OFFSET);
|
||||
__raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const struct file_operations ixp4xx_wdt_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.write = ixp4xx_wdt_write,
|
||||
.unlocked_ioctl = ixp4xx_wdt_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.open = ixp4xx_wdt_open,
|
||||
.release = ixp4xx_wdt_release,
|
||||
};
|
||||
|
||||
static struct miscdevice ixp4xx_wdt_miscdev = {
|
||||
.minor = WATCHDOG_MINOR,
|
||||
.name = "watchdog",
|
||||
.fops = &ixp4xx_wdt_fops,
|
||||
};
|
||||
|
||||
static int __init ixp4xx_wdt_init(void)
|
||||
static int ixp4xx_wdt_stop(struct watchdog_device *wdd)
|
||||
{
|
||||
struct ixp4xx_wdt *iwdt = to_ixp4xx_wdt(wdd);
|
||||
|
||||
__raw_writel(IXP4XX_WDT_KEY, iwdt->base + IXP4XX_OSWK_OFFSET);
|
||||
__raw_writel(0, iwdt->base + IXP4XX_OSWE_OFFSET);
|
||||
__raw_writel(0, iwdt->base + IXP4XX_OSWK_OFFSET);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ixp4xx_wdt_set_timeout(struct watchdog_device *wdd,
|
||||
unsigned int timeout)
|
||||
{
|
||||
wdd->timeout = timeout;
|
||||
if (watchdog_active(wdd))
|
||||
ixp4xx_wdt_start(wdd);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct watchdog_ops ixp4xx_wdt_ops = {
|
||||
.start = ixp4xx_wdt_start,
|
||||
.stop = ixp4xx_wdt_stop,
|
||||
.set_timeout = ixp4xx_wdt_set_timeout,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static const struct watchdog_info ixp4xx_wdt_info = {
|
||||
.options = WDIOF_KEEPALIVEPING
|
||||
| WDIOF_MAGICCLOSE
|
||||
| WDIOF_SETTIMEOUT,
|
||||
.identity = KBUILD_MODNAME,
|
||||
};
|
||||
|
||||
/* Devres-handled clock disablement */
|
||||
static void ixp4xx_clock_action(void *d)
|
||||
{
|
||||
clk_disable_unprepare(d);
|
||||
}
|
||||
|
||||
static int ixp4xx_wdt_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct ixp4xx_wdt *iwdt;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* FIXME: we bail out on device tree boot but this really needs
|
||||
* to be fixed in a nicer way: this registers the MDIO bus before
|
||||
* even matching the driver infrastructure, we should only probe
|
||||
* detected hardware.
|
||||
*/
|
||||
if (of_have_populated_dt())
|
||||
return -ENODEV;
|
||||
if (!(read_cpuid_id() & 0xf) && !cpu_is_ixp46x()) {
|
||||
pr_err("Rev. A0 IXP42x CPU detected - watchdog disabled\n");
|
||||
|
||||
dev_err(dev, "Rev. A0 IXP42x CPU detected - watchdog disabled\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
boot_status = (*IXP4XX_OSST & IXP4XX_OSST_TIMER_WARM_RESET) ?
|
||||
WDIOF_CARDRESET : 0;
|
||||
ret = misc_register(&ixp4xx_wdt_miscdev);
|
||||
if (ret == 0)
|
||||
pr_info("timer heartbeat %d sec\n", heartbeat);
|
||||
return ret;
|
||||
|
||||
iwdt = devm_kzalloc(dev, sizeof(*iwdt), GFP_KERNEL);
|
||||
if (!iwdt)
|
||||
return -ENOMEM;
|
||||
iwdt->base = dev->platform_data;
|
||||
|
||||
/*
|
||||
* Retrieve rate from a fixed clock from the device tree if
|
||||
* the parent has that, else use the default clock rate.
|
||||
*/
|
||||
clk = devm_clk_get(dev->parent, NULL);
|
||||
if (!IS_ERR(clk)) {
|
||||
ret = clk_prepare_enable(clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = devm_add_action_or_reset(dev, ixp4xx_clock_action, clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
iwdt->rate = clk_get_rate(clk);
|
||||
}
|
||||
if (!iwdt->rate)
|
||||
iwdt->rate = IXP4XX_TIMER_FREQ;
|
||||
|
||||
iwdt->wdd.info = &ixp4xx_wdt_info;
|
||||
iwdt->wdd.ops = &ixp4xx_wdt_ops;
|
||||
iwdt->wdd.min_timeout = 1;
|
||||
iwdt->wdd.max_timeout = U32_MAX / iwdt->rate;
|
||||
iwdt->wdd.parent = dev;
|
||||
/* Default to 60 seconds */
|
||||
iwdt->wdd.timeout = 60U;
|
||||
watchdog_init_timeout(&iwdt->wdd, 0, dev);
|
||||
|
||||
if (__raw_readl(iwdt->base + IXP4XX_OSST_OFFSET) &
|
||||
IXP4XX_OSST_TIMER_WARM_RESET)
|
||||
iwdt->wdd.bootstatus = WDIOF_CARDRESET;
|
||||
|
||||
ret = devm_watchdog_register_device(dev, &iwdt->wdd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
dev_info(dev, "IXP4xx watchdog available\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit ixp4xx_wdt_exit(void)
|
||||
{
|
||||
misc_deregister(&ixp4xx_wdt_miscdev);
|
||||
}
|
||||
|
||||
|
||||
module_init(ixp4xx_wdt_init);
|
||||
module_exit(ixp4xx_wdt_exit);
|
||||
static struct platform_driver ixp4xx_wdt_driver = {
|
||||
.probe = ixp4xx_wdt_probe,
|
||||
.driver = {
|
||||
.name = "ixp4xx-watchdog",
|
||||
},
|
||||
};
|
||||
module_platform_driver(ixp4xx_wdt_driver);
|
||||
|
||||
MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
|
||||
MODULE_DESCRIPTION("IXP4xx Network Processor Watchdog");
|
||||
|
||||
module_param(heartbeat, int, 0);
|
||||
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds (default 60s)");
|
||||
|
||||
module_param(nowayout, bool, 0);
|
||||
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started");
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -14,8 +14,8 @@ struct ixp4xx_pata_data {
|
|||
volatile u32 *cs1_cfg;
|
||||
unsigned long cs0_bits;
|
||||
unsigned long cs1_bits;
|
||||
void __iomem *cs0;
|
||||
void __iomem *cs1;
|
||||
void __iomem *cmd;
|
||||
void __iomem *ctl;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Add table
Reference in a new issue