Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable I2C#6 instead of I2C#8 #12

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion arch/x86/cpu/tangier/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
#
# Copyright (c) 2017 Intel Corporation

obj-y += car.o tangier.o sdram.o sysreset.o
obj-y += car.o tangier.o sdram.o sysreset.o pinmux.o
obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o
144 changes: 144 additions & 0 deletions arch/x86/cpu/tangier/pinmux.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, like I said in the bug report, check pinctrl-sandbox.c for a hint how this should look like. (Yes, I understand that we are creating a limited driver, it needs to be taken into account)

Copy link
Author

@staroselskii staroselskii Aug 30, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. So we're making it the UCLASS_PINCTRL, then? With some of pinctrl_ops implemented? And then we'll configure the pins in edison.c using the pinctrl API? Is this the plan? If so, I'll need some time adapting this code to the pinctrl API.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure that this what upstream ask us during review. However, we can try first with reduced solution. I guess, we may send it upstream and see what they will tell. In any case it would be nice to have your stuff in my tree sooner.

/*
* Copyright (c) 2018 Emlid Limited
*/

#include <common.h>
#include <dm.h>
#include <regmap.h>
#include <syscon.h>
#include <asm/cpu.h>
#include <asm/scu.h>
#include <linux/io.h>

#define BUFCFG_OFFSET 0x100

#define MRFLD_FAMILY_LEN 0x400

#define pin_to_bufno(f, p) ((p) - (f)->pin_base)

struct mrfld_family {
unsigned int family_number;
unsigned int pin_base;
size_t npins;
void __iomem *regs;
};

#define MRFLD_FAMILY(b, s, e) \
{ \
.family_number = (b), \
.pin_base = (s), \
.npins = (e) - (s) + 1, \
}

static struct mrfld_family mrfld_families[] = {
MRFLD_FAMILY(7, 101, 114),
};

struct mrfld_pinctrl {
const struct mrfld_family *families;
size_t nfamilies;
};

static const struct mrfld_family *
mrfld_get_family(struct mrfld_pinctrl *mp, unsigned int pin)
{
const struct mrfld_family *family;
unsigned int i;

for (i = 0; i < mp->nfamilies; i++) {
family = &mp->families[i];
if (pin >= family->pin_base &&
pin < family->pin_base + family->npins)
return family;
}

printf("failed to find family for pin %u\n", pin);
return NULL;
}

static void __iomem *
mrfld_get_bufcfg(struct mrfld_pinctrl *pinctrl, unsigned int pin)
{
const struct mrfld_family *family;
unsigned int bufno;

family = mrfld_get_family(pinctrl, pin);
if (!family)
return NULL;

bufno = pin_to_bufno(family, pin);

return family->regs + BUFCFG_OFFSET + bufno * 4;
}

static void
mrfld_setup_families(void *base_addr, struct mrfld_family *families, unsigned int nfam)
{
for (int i = 0; i < nfam; i++) {
struct mrfld_family *family = &families[i];

family->regs = base_addr + family->family_number * MRFLD_FAMILY_LEN;
}
}

int mrfld_pinconfig_protected(unsigned int pin, u32 mask, u32 bits)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an API it has to get a pin control device first, something like

    ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);

The idea behind is to get lazy probe (that's called "Driver Model" in U-Boot).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@andy-shev
Thanks. Makes sense.

But it seems like probe is already automagically called just by the sheer addition of the probe callback, doesn't it? I'm pretty sure I'm missing something, of course.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you call function before device is probed, it wouldn't work and crash everything. ->probe() will be called when we try to get pointer to a device.

{
struct mrfld_pinctrl *pinctrl;
struct udevice *dev;
void __iomem *bufcfg;
u32 v, value;
int ret;

ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev);
if (ret)
return ret;

pinctrl = dev_get_priv(dev);

bufcfg = mrfld_get_bufcfg(pinctrl, pin);
if (!bufcfg)
return -EINVAL;

value = readl(bufcfg);

v = (value & ~mask) | (bits & mask);

debug("scu: v: 0x%x p: 0x%x bits: %d, mask: %d bufcfg: 0x%p\n",
v, (u32)bufcfg, bits, mask, bufcfg);

ret = scu_ipc_raw_command(IPCMSG_INDIRECT_WRITE, 0, &v, 4,
NULL, 0, (u32)bufcfg, 0);
if (ret)
pr_err("Failed to set mode via SCU for pin %u (%d)\n", pin, ret);

return ret;
}

static int tangier_pinctrl_probe(struct udevice *dev)
{
void *base_addr = syscon_get_first_range(X86_SYSCON_PINCONF);

struct mrfld_pinctrl *pinctrl = dev_get_priv(dev);

mrfld_setup_families(base_addr, mrfld_families,
ARRAY_SIZE(mrfld_families));

pinctrl->families = mrfld_families;
pinctrl->nfamilies = ARRAY_SIZE(mrfld_families);

return 0;
}

static const struct udevice_id tangier_pinctrl_match[] = {
{ .compatible = "intel,pinctrl-tangier", .data = X86_SYSCON_PINCONF },
{ /* sentinel */ }
};

U_BOOT_DRIVER(tangier_pinctrl) = {
.name = "tangier_pinctrl",
.id = UCLASS_SYSCON,
.of_match = tangier_pinctrl_match,
.probe = tangier_pinctrl_probe,
.priv_auto_alloc_size = sizeof(struct mrfld_pinctrl),
};
5 changes: 5 additions & 0 deletions arch/x86/dts/edison.dts
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,9 @@
compatible = "intel,reset-tangier";
u-boot,dm-pre-reloc;
};

pinctrl {
compatible = "intel,pinctrl-tangier";
reg = <0xff0c0000 0x8000>;
};
};
10 changes: 10 additions & 0 deletions arch/x86/include/asm/arch-tangier/acpi/southcluster.asl
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,16 @@ Device (PCI0)
}
}

Device (I2C6)
{
Name (_ADR, 0x00090001)

Method (_STA, 0, NotSerialized)
{
Return (STA_VISIBLE)
}
}

Device (GPIO)
{
Name (_ADR, 0x000c0000)
Expand Down
4 changes: 4 additions & 0 deletions arch/x86/include/asm/scu.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#define _X86_ASM_SCU_IPC_H_

/* IPC defines the following message types */
#define IPCMSG_INDIRECT_READ 0x02
#define IPCMSG_INDIRECT_WRITE 0x05
#define IPCMSG_WARM_RESET 0xf0
#define IPCMSG_COLD_RESET 0xf1
#define IPCMSG_SOFT_RESET 0xf2
Expand All @@ -23,5 +25,7 @@ struct ipc_ifwi_version {
/* Issue commands to the SCU with or without data */
int scu_ipc_simple_command(u32 cmd, u32 sub);
int scu_ipc_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out, int outlen);
int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out,
int outlen, u32 dptr, u32 sptr);

#endif /* _X86_ASM_SCU_IPC_H_ */
35 changes: 35 additions & 0 deletions arch/x86/lib/scu.c
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,41 @@ static int scu_ipc_cmd(struct ipc_regs *regs, u32 cmd, u32 sub,
return err;
}

int scu_ipc_raw_command(u32 cmd, u32 sub, u32 *in, int inlen, u32 *out,
int outlen, u32 dptr, u32 sptr)
{
int inbuflen = DIV_ROUND_UP(inlen, 4);
struct udevice *dev;
struct scu *scu;
int ret;

ret = syscon_get_by_driver_data(X86_SYSCON_SCU, &dev);
if (ret)
return ret;

scu = dev_get_priv(dev);

/* Up to 16 bytes */
if (inbuflen > 4)
return -EINVAL;

writel(dptr, &scu->regs->dptr);
writel(sptr, &scu->regs->sptr);

/*
* SRAM controller doesn't support 8-bit writes, it only
* supports 32-bit writes, so we have to copy input data into
* the temporary buffer, and SCU FW will use the inlen to
* determine the actual input data length in the temporary
* buffer.
*/

u32 inbuf[4] = {0};

memcpy(inbuf, in, inlen);

return scu_ipc_cmd(scu->regs, cmd, sub, inbuf, inlen, out, outlen);
}
/**
* scu_ipc_simple_command() - send a simple command
* @cmd: command
Expand Down
27 changes: 27 additions & 0 deletions board/intel/edison/edison.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
/* List of Intel Tangier LSSs */
#define PMU_LSS_TANGIER_SDIO0_01 1

/* List of Intel Tangier pins */
#define MRFLD_I2C6_SCL 111
#define MRFLD_I2C6_SDA 112

/* These are taken from Linux kernel */
#define MRFLD_PINMODE_MASK 0x07
#define MRFLD_PIN_BITS 0x01

void board_mmc_power_init(void)
{
pmu_turn_power(PMU_LSS_TANGIER_SDIO0_01, true);
Expand Down Expand Up @@ -99,6 +107,17 @@ static void assign_hardware_id(void)
#endif
}

int mrfld_i2c6_setup(void)
{
u32 mask = MRFLD_PINMODE_MASK;
u32 bits = MRFLD_PIN_BITS;

mrfld_pinconfig_protected(MRFLD_I2C6_SCL, mask, bits);
mrfld_pinconfig_protected(MRFLD_I2C6_SDA, mask, bits);

return 0;
}

int board_late_init(void)
{
if (!env_get("serial#"))
Expand All @@ -107,5 +126,13 @@ int board_late_init(void)
if (!env_get("hardware_id"))
assign_hardware_id();

/*
* Initial configuration came from the firmware.
* Which quite likely has been used in the phones, where I2C #8,
* that is not part of Atom peripheral, is in use.
* Thus we need to override the leftover.
*/
mrfld_i2c6_setup();

return 0;
}