18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Generic SH-4 / SH-4A PCIC operations (SH7751, SH7780).
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2002 - 2009  Paul Mundt
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/pci.h>
88c2ecf20Sopenharmony_ci#include <linux/io.h>
98c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
108c2ecf20Sopenharmony_ci#include <asm/addrspace.h>
118c2ecf20Sopenharmony_ci#include "pci-sh4.h"
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci/*
148c2ecf20Sopenharmony_ci * Direct access to PCI hardware...
158c2ecf20Sopenharmony_ci */
168c2ecf20Sopenharmony_ci#define CONFIG_CMD(bus, devfn, where) \
178c2ecf20Sopenharmony_ci	(0x80000000 | (bus->number << 16) | (devfn << 8) | (where & ~3))
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/*
208c2ecf20Sopenharmony_ci * Functions for accessing PCI configuration space with type 1 accesses
218c2ecf20Sopenharmony_ci */
228c2ecf20Sopenharmony_cistatic int sh4_pci_read(struct pci_bus *bus, unsigned int devfn,
238c2ecf20Sopenharmony_ci			   int where, int size, u32 *val)
248c2ecf20Sopenharmony_ci{
258c2ecf20Sopenharmony_ci	struct pci_channel *chan = bus->sysdata;
268c2ecf20Sopenharmony_ci	unsigned long flags;
278c2ecf20Sopenharmony_ci	u32 data;
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci	/*
308c2ecf20Sopenharmony_ci	 * PCIPDR may only be accessed as 32 bit words,
318c2ecf20Sopenharmony_ci	 * so we must do byte alignment by hand
328c2ecf20Sopenharmony_ci	 */
338c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pci_config_lock, flags);
348c2ecf20Sopenharmony_ci	pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR);
358c2ecf20Sopenharmony_ci	data = pci_read_reg(chan, SH4_PCIPDR);
368c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pci_config_lock, flags);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	switch (size) {
398c2ecf20Sopenharmony_ci	case 1:
408c2ecf20Sopenharmony_ci		*val = (data >> ((where & 3) << 3)) & 0xff;
418c2ecf20Sopenharmony_ci		break;
428c2ecf20Sopenharmony_ci	case 2:
438c2ecf20Sopenharmony_ci		*val = (data >> ((where & 2) << 3)) & 0xffff;
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	case 4:
468c2ecf20Sopenharmony_ci		*val = data;
478c2ecf20Sopenharmony_ci		break;
488c2ecf20Sopenharmony_ci	default:
498c2ecf20Sopenharmony_ci		return PCIBIOS_FUNC_NOT_SUPPORTED;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci/*
568c2ecf20Sopenharmony_ci * Since SH4 only does 32bit access we'll have to do a read,
578c2ecf20Sopenharmony_ci * mask,write operation.
588c2ecf20Sopenharmony_ci * We'll allow an odd byte offset, though it should be illegal.
598c2ecf20Sopenharmony_ci */
608c2ecf20Sopenharmony_cistatic int sh4_pci_write(struct pci_bus *bus, unsigned int devfn,
618c2ecf20Sopenharmony_ci			 int where, int size, u32 val)
628c2ecf20Sopenharmony_ci{
638c2ecf20Sopenharmony_ci	struct pci_channel *chan = bus->sysdata;
648c2ecf20Sopenharmony_ci	unsigned long flags;
658c2ecf20Sopenharmony_ci	int shift;
668c2ecf20Sopenharmony_ci	u32 data;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	raw_spin_lock_irqsave(&pci_config_lock, flags);
698c2ecf20Sopenharmony_ci	pci_write_reg(chan, CONFIG_CMD(bus, devfn, where), SH4_PCIPAR);
708c2ecf20Sopenharmony_ci	data = pci_read_reg(chan, SH4_PCIPDR);
718c2ecf20Sopenharmony_ci	raw_spin_unlock_irqrestore(&pci_config_lock, flags);
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	switch (size) {
748c2ecf20Sopenharmony_ci	case 1:
758c2ecf20Sopenharmony_ci		shift = (where & 3) << 3;
768c2ecf20Sopenharmony_ci		data &= ~(0xff << shift);
778c2ecf20Sopenharmony_ci		data |= ((val & 0xff) << shift);
788c2ecf20Sopenharmony_ci		break;
798c2ecf20Sopenharmony_ci	case 2:
808c2ecf20Sopenharmony_ci		shift = (where & 2) << 3;
818c2ecf20Sopenharmony_ci		data &= ~(0xffff << shift);
828c2ecf20Sopenharmony_ci		data |= ((val & 0xffff) << shift);
838c2ecf20Sopenharmony_ci		break;
848c2ecf20Sopenharmony_ci	case 4:
858c2ecf20Sopenharmony_ci		data = val;
868c2ecf20Sopenharmony_ci		break;
878c2ecf20Sopenharmony_ci	default:
888c2ecf20Sopenharmony_ci		return PCIBIOS_FUNC_NOT_SUPPORTED;
898c2ecf20Sopenharmony_ci	}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	pci_write_reg(chan, data, SH4_PCIPDR);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return PCIBIOS_SUCCESSFUL;
948c2ecf20Sopenharmony_ci}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistruct pci_ops sh4_pci_ops = {
978c2ecf20Sopenharmony_ci	.read		= sh4_pci_read,
988c2ecf20Sopenharmony_ci	.write		= sh4_pci_write,
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ciint __attribute__((weak)) pci_fixup_pcic(struct pci_channel *chan)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	/* Nothing to do. */
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
106